PUN을 임포트할 때 "Wizard" 윈도우가 팝업 됩니다. 이메일 주소를 입력하여 클라우드에 등록하거나 이 단계를 건너띄고 기존 계정의 AppId 를 입력 또는 "self hosted" Photon 으로 전환하여 서버의 주소를 입력 합니다.
이렇게 하면 프로젝트에서 클라우드 서비스 또는 자신의 Photon 서버에 대한 설정파일이 생성 됩니다: PhotonServerSettings
PUN은 꽤 많은 파일로 구성되어 있지만 정말 중요한 파일은 PhotonNetwork 입니다. 이 클래스 안에는 필요한 모든 함수와 변수들이 있습니다. 커스텀 요구사항이 있다면 이 소스 파일을 언제든지 변경 할 수 있습니다.
UnityScript에서 PUN 을 사용하려면 "PhotonNetwork" 과 "UtilityScripts" 폴더를 Assets\Plugins\ 으로 이동 시켜 주시기 바랍니다.
이 API 이 어떻게 동작하는지 다음의 예를 통하여 바로 볼 수 있습니다.
접속과 마스터 서버
PhotonNetwork는 항상 한대의 마스터 서버와 한대 이상의 게임서버를 사용 합니다. 마스터 서버의 역할은 현재 이용 할 수 있는 게임들과 매치 메이킹을 관리 하는것 입니다. 룸이 있거나 생성 되었으면 실제 게임 플레이는 게임 서버에서 수행 됩니다.
모든 서버들은 전용 머신에서 수행 됩니다 - 플레이어가 호스트하고 있는 서버들은 없습니다. 서버 구성을 pun이 알아서 관리해주므로 기억할 필요가 없습니다.
1 PhotonNetwork.ConnectUsingSettings("v1.0");
위 코드 한 줄이 Photon 에 연결하고 Photon의 기능을 활용하는데 필요한 전부 입니다. 위 코드는 클라이언트의 게임 버전을 설정하고 연결 할 때 PhotonServerSettings(PUN Wizard 에서 만들어진)이 사용 됩니다. 나만의 서버에 접속 하기 위해서 PhotonServerSettings 을 변경 할 수 도 있습니다. 또한 PhotonServerSettings 파일을 무시하고 접속 하고 싶은 경우에는 Connect() 를 이용 합니다.
게임 버전을 통한 버전 관리
Photon 의 로드밸런싱 로직은 AppId 를 통해 다른 사람의 게임으로 부터 내 게임을 구별 합니다. 플레이어들은 게임 버전에 의해서 또 다시 분리 됩니다 위의 ConnectUsingSettings 파라미터를 참조 하세요. 이 방식으로 신규 기능을 가진 새로운 버전의 클라이언트를 이전 버전을 사용하고 있는 클라이언트 게임 접속을 끊김 없이 출시 할 수 있습니다.
서로 다른 Photon Unity Networking 버전 간의 호환성을 보장 할 수 없으므로, PUN은 게임 버전에 자신의 버전 번호를 추가합니다. PUN을 업데이트하면 이전 클라이언트와 새로운 클라이언트가 분리 되지만 이전 클라이언트가 이용할 수 없는 것은 아닙니다.
게임 생성 및 참가
이제 룸에 참가하거나 생성 하고 싶을 것 입니다. 다음 코드는 필요한 함수들을 보여 줍니다:
Code Example C#:
1 //Join a room
2 PhotonNetwork.JoinRoom(roomName);
3
4 //Create this room.
5 PhotonNetwork.CreateRoom(roomName);
6 // Fails if it already exists and calls: OnPhotonCreateGameFailed
7
8 //Tries to join any random game:
9 PhotonNetwork.JoinRandomRoom();
10 //Fails if there are no matching games: OnPhotonRandomJoinFailed
가장 좋은 경우는 게임이 무작위 매치 메이킹을 사용하는 것입니다. JoinRandomRoom() 은 무작위로 룸에 참여하려고 시도 합니다. 만약 실패 한 경우(플레이어를 수용하는 룸이 없는 경우) 새로운 룸을 생성하고 다른 플레이어들이 무작위로 참여 할 때 까지 대기 하게 됩니다.
또 다른 방식으로는 클라이언트들이 이용할 수 있는 룸의 목록을 받는 것입니다. 룸 목록에 대한 수신은 로비에 참여할 때 이루어 집니다.
로비들은 로비안에 있는 룸의 목록을 클라이언트에게 자동적으로 전송하며 특정 시간 간격(트래픽 최소화를 하기 위해)마다 룸 목록을 갱신 합니다. 플레이어들은 플레이어를 볼 수 없으며 서로 소통을 할 수 없습니다(게임이 혼잡할 때의 문제 방지를 위해서).
PhotonNetwork 플러그인은 연결 되었을 때 자동적으로 디폴트 로비에 참가 할 수 있습니다. PhotonServerSettings 파일에 있는 "Auto-Join Lobby" 체크박스를 체크하여 주시기 바랍니다.
클라이언트가 로비내에 있을 동안 룸 목록에 대한 갱신 정보를 받게 되며 캐시 합니다. 필요하다면 매 프레임마다 GetRoomList 를 이용하여 룸의 리스트를 얻을 수 있습니다.
Code Example C#:
1 foreach (RoomInfo room in PhotonNetwork.GetRoomList())
PhotonNetwork 는 " 연결" 또는 "게임에 참가" 와 같은 상태 변화를 인지 하기 위하여 몇 개의 콜백을 사용 합니다. 일반적으로 Unity 에서는 스크립트에서 콜백을 구현 할 수 있습니다.
만약 스크립트가 Photon.PunBehaviour 에서 상속을 받으면 개별적으로 각각의 콜백들을 오버라이드(override)할 수 있습니다. 이 경우에는 베이스 클래스 구현 코드를 호출 할 필요가 없습니다.
Code Example C#:
1 public override void OnJoinedRoom()
2 {
3 Debug.Log("OnJoinedRoom() called by PUN: " + PhotonNetwork.room.name);
4 }
PunBehaviour 에서 상속을 받을 필요가 없습니다. 모든 콜백들은 자신이 구현하면 동작하게 됩니다. 콜백들은 PhotonNetworkingMessage 에 enum 으로 나열되어 설명되어 있습니다.
여기에서는 게임 룸에 대한 기본 설정에 대해서만 다루고 있습니다. 다음은 게임내에서 실제 커뮤니커에션에 대해서 살펴 봅니다.
게임 룸에 메시지 전송
룸 안에서 연결된 다른 플레이어들에게 네트워크 메시지를 전송 할 수 있습니다. 또한 나중에 접속 할 플레이어(예를 들어 플레이어를 스폰하는 것)에게 전송할 수 있도록 버퍼화된 메시지도 보낼 수 있습니다.
메시지는 두 가지 방법을 사용하여 전송 할 수 있습니다. RPC를 이용하거나 또는 PhotonView 에 의해서 관찰되고 있는 스크립트내의 OnSerializePhotonView 를 구현 하는 방법 입니다.
하지만 여기에 더 많은 네트워크 작용이 있습니다. OnPhotonInstantiate 또는 OnPhotonPlayerConnected 등과 같은 특정한 네트워크 이벤트에 대한 콜백을 Listen 할 수 있고 이러한 이벤트를 트리거 시킬 수 있습니다. 예,PhotonNetwork.Instantiate
앞으로 이 주제들에 대해서 다룰 것이니 너무 혼란 스러워 하지 마세요.
PhotonView
PhotonView는 메시지 전송을 위한 스크립트 컴포넌트 입니다(RPC와 OnSerializePhotonView). 게임오브젝트나 프리팹에 PhotonView 를 붙여 주세요. PhotonView 는 유니티의 NetworkView 와 매우 유사합니다.
메시지 전송과 선택적으로 인스턴스 생성/다른 PhotonView 들의 할당을 위하여 항상 게임 에서는 최소 하나의 PhotonView 가 있어야 합니다.
게임오브젝트에 PhotonView 를 추가하기 위해서는 게임오브젝트를 선택하고 "Components/Miscellaneous/Photon View" 를 사용합니다.
Photon Cloud: Photon View
Observe Transform
PhotonView 의 Observe 프로퍼티에 트랜스폼을 추가하면 플레이어들의 위치, 회전과 확대비율 또는 이 모든 것을 조합한 정보를 동기화 할 수 있도록 선택 할 수 있습니다. 프로토타이핑 또는 규모가 작은 게임에 큰 도움을 줄 수 있습니다. 노트 : 관찰되고 있는 값이 변경되면 모든 관찰 값이 전송 됩니다 - 변경된 단일 값만이 아닙니다. 또한 변경된 사항은 평활화 되거나 보간 되지 않습니다.
Observe MonoBehaviour
PhotonView 는 MonoBehaviour를 관찰하도록 설정 될 수 있습니다. 이 경우에는 스크립트의 OnPhotonSerializeView 메소드가 호출 될 것 입니다. 메시지는 이 스크립트가 로컬 플레이어에 의해 제어 되는지에 따라 객체의 상태를 쓰고 읽기 위해 호출 됩니다.
아래는 몇 줄 안되는 코드로 캐릭터 상태 동기화 추가를 어떻게 하는지 간단히 보여주고 있습니다.
Observe Option 필드를 통해 변경사항이 어떤 방식으로 언제 전송되는 지를 선택 합니다. 그리고 OnPhotonSerializeView 이 얼마나 자주 호출되는지에 영향을 줍니다.
Off 말 그대로 PhotonView 가 RPC 전용으로 되어 있을 때 유용합니다.
Unreliable (신뢰할 수 없는) 업데이트는 "있는 그대로" 전송되지만 손실 될 수 있습니다. 다음 업데이트가 신속하게 도착 하여 정확한 값을 제공 해 준다는 개념입니다. 이것은 위치 및 잘 변하지 않는 데이터에 최적이지만, 무기 바꾸는 것과 같은 트리거에는 적합하지 않습니다. 게임 오브젝트의 위치를 동기화 하는 경우, 게임 오브젝트가 이동 하지 않아도 항상 업데이트를 전송 하게 됩니다(좋지 않은 방식 입니다).
Unreliable on Change 은 업데이트마다 변경을 확인합니다. 모든 값이 이전에 보낸 값과 같다면 하나의 업데이트가 reliable (신뢰할 수있는)로 전송되고 소유자는 다시 값이 변경이 있을 때 까지 전송을 중지합니다. 이 방식은 게임 오브젝트가 멈춰있을 때 잠시 동안은 더 이상의 갱신정보를 생성하지 않으므로 매우 유용합니다. 박스처럼 특정 지점에서 더 이상 움직이지 않는 것처럼 말이죠.
Reliable Delta Compressed 는 갱신된 값을 이전의 값과 비교 합니다. 트래픽을 낮게 유지 하기 위하여 변경되지 않은 값은 생략됩니다. 수신측에서는 단순하게 이전의 값을 교체합니다. OnPhotonSerializeView를 이용해 시리얼라이징 하면 자동적으로 이 방식으로 체크 되며 압축됩니다. 변경 사항이 없다면 수신 측 클라이언트에서 OnPhotonSerializeView 이 호출 되지 않습니다. "reliable" 은 데이터 전송시에 부하가 커지므로 변경 사항이 적은 경우 오버 헤드를 고려 해 주어야 합니다.
이제 RPC를 사용하여 통신하는 방법에 대해 설명합니다.
Remote Procedure Calls (원격 프로시져 호출)
Remote Procedure Calls (RPC) 은 "네트워크 게임오브젝트"의 메소드를 호출 할 수 있고 사용자 입력에 의해서 발생되는 빈번하지 않은 액션에 매우 유용하게 사용할 수 있습니다.
RPC 는 동일한 룸 내에 있는 각각의 플레이어들에 의해 동일한 게임오브젝트에서 수행 되기 때문에 특정 'GameObject' 를 변경하여 전체 Scene 에 대한 효과를 쉽게 트리거 할 수 있습니다.
'RPC'는 같은 방 각 플레이어에 의해 일같은 게임 개체에 실행되기 때문에, GameObject를 변경하여 장면의 효과를 쉽게 트리거 할 수 있습니다.
RPC로 호출되는 메소드들은 게임 오브젝트에 PhotonView 컴포넌트가 있어야 합니다. 메소드 자체는 [PunRPC] 속성으로 마킹 되어야 합니다.
Code Example C#:
1 [PunRPC]
2 void ChatMessage(string a, string b)
3 {
4 Debug.Log("ChatMessage " + a + " " + b);
5 }
메소드를 호출 하기 위해서 대상 객체의 PhotonView 에 접근 합니다. 대상 메소드를 직접 호출 하는 대신에 PhotonView.RPC() 를 호출하고 호출 할 메소드 이름을 알려 줍니다:
이것이 기본 입니다. Remote Procedure Calls 에 대해서 문서를 더 읽어 보세요.
RPC의 타이밍과 로딩 레벨
RPC는 특정 PhotonViews에서 호출 되며 수신측에서 매칭되는 하나의 클라이언트가 목표 지점 입니다. 만약 원격 클라이언트가 로드되지 않았거나 아직 매칭되는 PhotonView 생성을 하지 못했으면 RPC 는 손실 됩니다!
이 때문에 RPC 손실에 대한 전형적인 원인은 클라이언트가 새로운 신(scene)을 로드 할 때 입니다. 하나의 클라이언트는 새로운 게임오브젝트들과 함께 신을 로드 했으나 다른 클라이언트들이 신을 로드 하지 못했다면 이 클라이언트들은 RPC를 알 수가 없습니다(동일 신을 로드 하기 전까지)
PUN은 이것을 관리 할 수 있습니다. 접속 전에 PhotonNetwork.automaticallySyncScene = true 로 설정하고 룸의 마스터 클라이언트에게 PhotonNetwork.LoadLevel() 를 사용하시면 됩니다. 이 방식으로 하나의 클라이언트가 룸내의 모든 클라이언트들이 로드해야할 레벨을 정의합니다.
RPC 손실을 방지 하기 위해서 클라이언트는 수신 되는 메시지 처리를 멈출 수 있습니다(LoadLevel이 해줍니다).
RPC의 전송 실패를 방지하기 위해 클라이언트가 보내 오는 RPC의 실행을 중지 할 수 있습니다 (즉 LoadLevel이 할 것입니다). 신을 로드 하라는 RPC 요청을 받았을 때 내용이 모두 초기화 되기 전까지 즉시 isMessageQueueRunning = false 를 설정 합니다.
예제:
Code Example C#:
1 private IEnumerator MoveToGameScene()
2 {
3 // Temporary disable processing of futher network messages
4 PhotonNetwork.isMessageQueueRunning = false;
5 Application.LoadLevel(levelName);
6 }
메시지 큐를 사용하지 않도록 설정 해놓으면 큐가 잠금 해제 될 때까지는 메시지의 송수신이 지연 될 것입니다.