해당 글은 유튜버 " 고라니TV - 게임개발 채널" 님의 "유니티 포톤PUN2 서버 개발" 재생목록을 참고하여 작성하였습니다.
https://www.youtube.com/watch?v=mPCNTi3Booo&list=PL3KKSXoBRRW3YE4UMnRH762vOhSHLdnpK
[ 환경 세팅 ]
1. 우선 https://www.photonengine.com/ko-kr 에 접속하여 회원가입/로그인을 한다.
2. 오른쪽 위 [ 관리 화면으로 이동 ] - [ 새 어플리케이션 만들기 ] 를 클릭한다.
3. Photon 종류는 [ Pun ] 으로 설정하고, 어플리케이션의 이름과 설명을 작성한 뒤 [ 작성하기 ] 를 클릭한다.
4. 어플리케이션 ID를 복사하고, Unity Asset Store에서 Photon Pun 2를 다운로드 후 import 해준다.
5. 복사한 어플리케이션 ID를 [ Assets ] - [ Photon ] - [ PhotonUnityNetworking ] - [ Resources ] - [ Photon Server Settings ]
- [ Server/Cloud Settings ] 의 [ App Id PUN ] 에 붙여넣는다.
[ Photon 기능 함수 ]
Photon 기능 함수
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Photon.Pun; // MonoBehaviourPunCallbacks를 상속 받기 위해 추가 using Photon.Realtime; // MonoBehaviourPunCallbacks를 상속 받기 위해 추가 public class NetworkManager : MonoBehaviourPunCallbacks // MonoBehaviour이 아닌 MonoBehaviourPunCallbacks를 상속 받는다. { public Text StatusText; public InputField roomInput, NickNameInput; void Awake() => Screen.SetResolution(960, 540, false); void Update() => StatusText.text = PhotonNetwork.NetworkClientState.ToString(); // PhotonNetwork.NetworkClientState.ToString()는 현재 상태를 문자열로 반환 public void Connect() => PhotonNetwork.ConnectUsingSettings(); // Photon Online Server에 접속 public override void OnConnectedToMaster() // Call back 함수 { print("서버접속완료"); PhotonNetwork.LocalPlayer.NickName = NickNameInput.text; } public void Disconnect() => PhotonNetwork.Disconnect(); // Photon Online Server와의 연결 끊기 public override void OnDisconnected(DisconnectCause cause) => print("연결끊김"); // Call back 함수 public void JoinLobby() => PhotonNetwork.JoinLobby(); // 로비에 접속 public override void OnJoinedLobby() => print("로비접속완료"); // Call back 함수 public void CreateRoom() => PhotonNetwork.CreateRoom(roomInput.text, new RoomOptions { MaxPlayers = 2 }); // 방 생성 (방 이름, 최대 플레이어 수, 비공개 지정 가능) public override void OnCreatedRoom() => print("방만들기완료"); // Call back 함수 public void JoinRoom() => PhotonNetwork.JoinRoom(roomInput.text); // 방 참가 (방 이름으로 입장 가능) public override void OnJoinedRoom() => print("방참가완료"); // Call back 함수 public void JoinOrCreateRoom() => PhotonNetwork.JoinOrCreateRoom(roomInput.text, new RoomOptions { MaxPlayers = 2 }, null); // 방 참가, 방이 없으면 생성후 참가 public void JoinRandomRoom() => PhotonNetwork.JoinRandomRoom(); // 방 랜덤 참가 public void LeaveRoom() => PhotonNetwork.LeaveRoom(); // 방 떠나기 public override void OnCreateRoomFailed(short returnCode, string message) => print("방만들기실패"); // Call back 함수 public override void OnJoinRoomFailed(short returnCode, string message) => print("방참가실패"); // Call back 함수 public override void OnJoinRandomFailed(short returnCode, string message) => print("방랜덤참가실패"); // Call back 함수 // [ Script Component ] - [ 오른쪽 마우스 ] - [ 정보 ] 를 통해 실행 가능 [ContextMenu("정보")] void Info() { if (PhotonNetwork.InRoom) { print("현재 방 이름 : " + PhotonNetwork.CurrentRoom.Name); print("현재 방 인원수 : " + PhotonNetwork.CurrentRoom.PlayerCount); print("현재 방 최대인원수 : " + PhotonNetwork.CurrentRoom.MaxPlayers); string playerStr = "방에 있는 플레이어 목록 : "; for (int i = 0; i < PhotonNetwork.PlayerList.Length; i++) playerStr += PhotonNetwork.PlayerList[i].NickName + ", "; print(playerStr); } else { print("접속한 인원 수 : " + PhotonNetwork.CountOfPlayers); print("방 개수 : " + PhotonNetwork.CountOfRooms); print("모든 방에 있는 인원 수 : " + PhotonNetwork.CountOfPlayersInRooms); print("로비에 있는지? : " + PhotonNetwork.InLobby); print("연결됐는지? : " + PhotonNetwork.IsConnected); } } }
[ RPC란? ]
- Player의 움직임을 동기화하기 위해서는 "Photon View"와 "Photon Transform View" Script를 Component로 추가해준다.
~> "Photon Transform View" Script Component 의 Synchronize Options 에서 무엇을 동기화할 것인지 선택할 수 있다.
(Position, Rotation, Scale 에 관한 동기화 선택이 가능하다.)
~> "Photon View" Script Component 의 Observed Components에 "Photon Transform View" Script Component 를
드래그 앤 드롭으로 연결한다. (이는 "Photon Transform View" Script Component 를 관찰하여 동기화 한다는 것)
~> 동기화는 "Photon View" Script Component 의 Controlled locally가 true인 경우에만 가능하다.
(Controlled locally는 PhotonView PV; PV.IsMine 으로 확인이 가능하다.)
- Position, Rotation, Scale 를 제외한 나머지는 어떻게 동기화 시킬까?
~> RPC 함수를 통해 동기화 시켜야 한다.
RPC 함수 선언 및 사용 예제using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; using Photon.Realtime; public class PlayerScript : MonoBehaviourPunCallbacks { public PhotonView PV; public SpriteRenderer SR; void Update() { if (PV.IsMine) { float axis = Input.GetAxisRaw("Horizontal"); transform.Translate(new Vector3(axis * Time.deltaTime * 7, 0, 0)); // RPC 함수 호출 (실행하고자 하는 함수 이름, 타겟, 인자) // RPCTarget.All은 그 즉시 호출되어 사라지지만, AllBuffered는 재접속될때 호출된다. if (axis != 0) PV.RPC("FlipXRPC", RpcTarget.AllBuffered, axis); } } // RPC 함수 선언 [PunRPC] void FlipXRPC(float axis) { SR.flipX = axis == -1; } }
[ 애니매이션 동기화 ]
- Animation을 동기화하기 위해서는 "Photon View"와 "Photon Animator View" Script를 Component로 추가해준다.
~> "Photon Animator View" Script Component 의 Synchronize Layer Weights 와 Synchronize Parameters 선택지 중
"Disabled" 는 "사용 안 함", "Discrete" 는 "On/Off시 호출", "Continuous" 는 "수시로 호출" 에 해당한다.
~> "Photon View" Script Component 의 Observed Components에 "Photon Animator View" Script Component 를
드래그 앤 드롭으로 연결한다. (이는 "Photon Animator View" Script Component 를 관찰하여 동기화 한다는 것)
[ 변수 동기화 ]
- 변수를 동기화하기 위해서는 MonoBehaviourPunCallbacks 뿐만이 아닌 IPunObservable 도 상속 받는다.
~> IPunObservable 를 상속 받는 경우 OnPhotonSerializeView 인터페이스 구현이 필수다.
~> 동기화는 PhotonView를 반드시 거쳐가야 하기 때문에 위의 Script를 "Photon View" Script Component 의 Observed
Components에 드래그 앤 드롭으로 연결한다.
변수 동기화 예제using System.Collections; using System.Collections.Generic; using UnityEngine; using Photon.Pun; using Photon.Realtime; using UnityEngine.UI; public class PlayerScript : MonoBehaviourPunCallbacks, IPunObservable // IPunObservable을 상속 받는다. { // ... public Text txt; // ... [ContextMenu("더하기")] public void Plus() => txt.text = (int.Parse(txt.text) + 1).ToString(); // OnPhotonSerializeView 인터페이스 구현 public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.IsWriting) stream.SendNext(txt.text); // Server에 보낼때 else txt.text = (string)stream.ReceiveNext(); // Server로부터 받을때 } }