게임엔진/Unity

[Unity] photon pun2

우향우@ 2023. 8. 3. 17:07

photon pun2

photon은 각종 멀티플레이용 네트워크 패키지를 제공해주는 회사이다. photon의 여러 서비스중 pun2는 Photon Unity Networking로 유니티용 멀티플레이 패키지 서비스이다.pun2는 photon에서 제공해주는 서버를 사용하여 클라이언트 측 코딩만 진행하여 멀티플레이 게임을 제작하는것이 가능하게 해준다.

pun2는 마치 p2p를 하는듯 마스터 플레이어와 그외 플레이어 사이의 통신처럼 인터페이스를 제공해주지만 실제로는 서버를 경유하는 서버 기반 멀티를 수행하게된다.

 

pun2 asset

pun2로 멀티게임을 만드려면 우선 photon서버에서 pun2 어플리케이션을 하나 생성하고 id를 받아야한다. 그러면 활성화된 pun2서버가 생겨나며 해당 서버로 멀티게임을 만들 수 있다.

그 다음 유니티 프로젝트에서 pun2 asset을 임포트한다. 그러면 에셋 폴더에 photon 폴더가 생긴다. 거기서 photon/PhotonUnityNetworking/resource/PhotonServerSettings의 디테일 패널에서 여러 값들을 설정할 수 있는데 add ID PUN에 아까 photon에서 할당받은 id를 입력하면 해당 서버를 사용하는 프로젝트가 되게 된다.

 

MonoBehaviourPunCallbacks

pun2의 기능을 사용할 컴포넌트를 만드려면 Photon.Pun을 using하고 monobehavior대신 MonoBehaviourPunCallbacks를 상속받는 클래스를 만들어 사용하면 된다. 그러면 해당 클래스는 컴포넌트로 사용되며 동시에 pun2의 각종 기능을 사용할 수 있다.

 

서버 접속

pun2는 총 세가지의 서버 계층을 가진다.

1. 서버 : 게임 전체 서버

2. 로비 : 룸들이 모여있는 채널

3. 룸 : 플레이어들이 모여서 실제 게임을 진행하는 게임방

 

서버 접속

pun2의 클라이언트 프로그램, 즉 유니티 프로젝트는 실행되면 우선 pun2 서버에 접속해야한다.

MonoBehaviourPunCallbacks컴포넌트에서 다음 함수로 해당 프로젝트의 pun2서버에 접속시도를 할 수 있다.

 

PhotonNetwork.ConnectingUsingSettings();

 

연결에 성공하면 다음 오버라이드 이벤트 함수가 호출된다.

override void OnConnectedToMaster()

해당 함수를 오버라이드하여 로직을 넣으면 연결 성공시 해당 로직이 수행된다.

 

로비 접속

pun2 서버에 접속을 성공했다면 클라이언트는 자동으로 디폴트 로비에 접속한 상태가 된다. 여기서 바로 디폴트 로비의 룸에 접속할수도 있고 아니면 다른 로비에 접속을 시도할 수 있다.

다음 함수로 다른 로비에 접속시도를 할 수 있다.

PhotonNetwork.joinLobby(typedLobby)

여기서 typedLobby객체는 로비 이름과 로비 타입으로 만들 수 있는 한 로비를 지칭하는 객체이다.

여기서 로비 이름은 string으로 지정되는데 빈 문자열이나 "null"을 사용하면 디폴트 로비를 지칭하게 된다.

로비 타입에는 3가지가 있지만 기본적으론 LobbyType.Default를 사용하여 디폴트 타입을 사용하면 된다.

만약 없는 로비에 접속을 시도하면 새 로비가 만들어지고 접속하게 된다.

보통 디폴트 로비만 사용하기에 로비에 직접 접속하는 로직은 보통 작성되지않는편이 많다.

 

마찬가지로 로비 접속 성공시

override void OnJoinedLobby()가 호출된다.

 

룸 접속

로비에 접속되었다면 다음 함수로 특정 룸에 접속 할 수 있다.

PhotonNetwork.joinRoom("방이름")

다음 함수로 룸에 접속하거나 없는 룸이면 생성하여 접속 할 수도 있다.

PhotonNetwork.joinOrCreateRoom("방이름")

그외에도 랜덤 방접속등의 다양한 방 접속 기능들이 존재한다.

 

룸에 접속하면 본격적으로 게임에 참가하게 되며 실제 게임이 진행되게 된다.

룸을 만든 클라이언트는 마스터 클라이언트가 되며 방장으로써의 기능을 하게된다.

마스터 클라이언트의 접속이 끊기면 다른 클라이언트에게 마스터가 넘어간다.

 

마찬가지로 룸 접속 성공시

override void OnJoinedRoom()이 호출된다.

 

 

Photon View

pun2에서는 Photon View라는 컴포넌트를 단 오브젝트들을 만들 수 있는데 이 오브젝트들은 각 클라이언트들간에 동기화가 되는 동기화 오브젝트이다. 해당 컴포넌트와 함께 Photon RigidBoby View, Photon animator view등의 각종 동기화 뷰들을 함께 컴포넌트로 넣으면 해당 요소들이 동기화 되게 된다. 이러한 오브젝트는 소유자가 존재하며 보통 소유자에 의해 컨트롤되고 다른 클라이언트들은 그것을 동기화 받는다.

 

이렇게 Photon View를 달고있는 오브젝트는 다음 두가지로 분류 할 수 있다.

1. 처음부터 프로젝트 월드에 배치되있던 경우

2. PhotonNetwork.Instantiate함수로 생성된 경우

 

1번의 경우 기본적으로 마스터 클라이언트가 소유한 하나의 오브젝트로 처리되며 다른 클라이언트들은 해당 오브젝트를 동기화 받는다.

2번의 경우 생성한 클라이언트가 소유하게 되며 다른 클라이언트들은 동기화를 받는다.

 

여기서 PhotonNetwork.Instantiate("프리팹 이름",위치, 회전)으로 오브젝트를 생성하면 각 클라이언트의 월드에 해당 오브젝트가 생성되며 생성자는 소유자로써, 나머지는 비 소유자로써 오브젝트가 생성되게 되는 셈이다.

1번도 마찬가지로 각 월드에 해당 오브젝트가 존재하지만 마스터만 소유자인 셈이다.

 

추가로 PhotonNetwork.Instantiate에서 사용되는 프리팹은 resource폴더안에 있어야하며 프리팹 파일 이름으로 매칭할 수 있다.

 

보통 Photon View이용한 캐릭터 조작은 다음과 같이 구현된다.

1. 룸에 접속시 PhotonNetwork.Instantiate로 자신의 캐릭터를 생성

2. 해당 캐릭터의 update에서 PhotonView컴포넌트.isMine으로 자신이 소유한 오브젝트인지를 확인

3. 자신의 오브젝트면 입력등으로 캐릭터를 조작

4.  Photon RigidBoby View등이 달려있다면 자동으로 각 클라이언트들에 조작이 동기화됨

 

커스텀 동기화

pun2에는 기본적으로 제공되는 view컴포넌트들로 특정 정보들을 동기화할 수 있다. 하지만 그외에도 rpc나 커스텀 동기화기능을 통해 자체적인 동기화도 만들 수 있다. 커스텀 동기화는 다음과 같이 구현 할 수 있다.

1. view 오브젝트에 붙어 동기화를 제공할 MonoBehaviourPunCallbacks에 IPunObservable인터페이스를 달아준다.

2. void onPhotonSerializerView(PhotonStream stream, PhotonMessageInfo info)함수를 오버라이드한다.

이때 onPhotonSerializerView에는 동기화를 하는 대상(해당 view를 소유한 플레이어)가 동기화를 받는 대상(그외 플레이어)들에게 정보를 전파해주는 기능을 구현해야한다. 이 함수안에서 전파와 전달이 둘다이루어진다. 이는 다음과 같이 한다.

1.stream.IsWrite로 전파자인지 피전파자인지 확인한다.

2. 전파자라면 Stream.SendNext(보낼 데이터)로 데이터를 보낸다. 여기서 타입은 상관없다.

3. 피전파자라면 Stream.RecieveNext()로 전파자가 퍼트린 데이터를 받아 적절하게 동기화한다. 여기서 받은 데이터는 적절한 타입으로 형변환 해야한다. (변환타입)Stream.RecieveNext()

 

RPC

RPC는 원격 프로시저 호출로 한 플레이어가 다른 플레이어들이 특정 함수를 호출하도록 하는 기능이다.

RPC를 사용하려면 다음과 같이 하면된다.

1. Photon View 오브젝트의 MonoBehaviourPunCallbacks 스크립트에서 [PunRPC]를 단 멤버 함수를 하나만든다.

2. 해당 스크립트에서 해당 오브젝트의 PhotonView컴포넌트오브젝트.RPC("함수명",함수호출대상플레이어,함수 매개변수...)를 호출하면 설정해준 대상 플레이어들의 씬의 해당 PhotonView 오브젝트의 해당 함수가 각자 호출된다.

 

여기서 함수 호출 대상 플레이어를 RpcTarget.All로 하면 자신 포함 모든 플레이어에서 호출되며 RpcTarget.AllBuffer로 하면 처리 결과를 버퍼에 저장해두고 이후 들어온 플레이어들도 호출되도록 해준다.