포톤으로 기존 싱글플레이 게임들을 멀티플레이 게임으로 만들어보자.
물론 기존에 만들어놨던 게임 구조가 너무나도 복잡하면…. 좀 힘들 수 있다.
주로 상속받는 부분 변경과 함수 변경에 대해 이야기해보고자 한다.
MonobehaviourPun vs MonobehaviourPunCallbacks
일단 제일 먼저 하게되는 일은
Monobehaviour
를 상속받고 있던 기존의 스크립트를
MonobehaviourPun
이나 MonobehaviourPunCallbacks
를 상속받는 걸로 바꿔주면서 시작한다.
둘의 차이는 뭐냐 하면, 오버라이드를 하는지 안하는지에 따라 다르다.
포톤으로 세팅하는 과정에서 포톤의 기능함수들을 오버라이드해서 사용할 일이 있다.
방을 세팅한다거나, 만들어진 방에 참가한다거나 하는 등의 기능이다.
따라서 보통 로비를 제작하여 플레이어들을 매칭하는 기능을 만드려고 할 때
주로 이 MonobehaviourPunCallbacks
를 사용하는 편이고
그게 아니라면 MonobehaviourPun
을 주로 사용한다.
보통은 PhotonView
로 동기화를 해준다.
대부분의 동기화는 이 PhotonView
에서 해결이 된다.
자세한 내용은 다음 글을 참고하자.
하지만, Transform, Rigidbody, Animator 외에 우리가 직접 작성한 스크립트의 변수라던지 저 세 가지에 해당되지 않는 컴포넌트를 동기화할 일이 있을 것이다.
그것을 위해 여러 방법들을 사용한다.
로컬 vs 리모트
현재 스크립트가 달려 있는 오브젝트가, 내가 조작하는 것인지 다른 플레이어가 조작하는 것인지 구분해줄 필요가 있다.
PhotonView는 부착할 때마다 고유의 ViewID를 부여해주는데, 이 ViewID의 소유권으로 로컬인지 리모트인지를 구분하게 된다.
메서드 상으로는 매우 간단한데,
photonView.IsMine
으로 확인하면 된다.
photonView.IsMine
은
해당 객체를 내가 생성한 것인지 아닌지를 반환하는 프로퍼티이다.
photonView.AmOwner
와 헷갈릴 수 있는데,
photonView.AmOwner
는 이 PhotonView의 Ownership(소유권)을 내가 가지고 있는지 아닌지를 반환해준다.
따라서 내가 생성하지 않은 객체라도 소유권을 넘겨받으면 내가 제어할 수 있도록 하기 위해 존재하는 프로퍼티이다.
따라서 로컬에서만 작동하는 메서드의 경우 앞에 이러한 구문을 붙여준다.
if (!photonView.IsMine) return;
변수 동기화: IPunObservable
IPunObservable
은
스크립트 내에서
특정 변수들을 동기화 할 일이 있을 때
사용하게 되는 인터페이스이다.
상속 시 필수 구현부는 단 하나밖에 없다.
OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
라는 함수이다.
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
// 변수를 변경하는 부분 (통신을 보내는)
stream.SendNext(changedInt); // 변수1
stream.SendNext(changedVec); // 변수2
}
else
{
// 바뀐 변수를 읽어오는 기능 (클론이 통신을 받도록)
changedInt = (int)stream.ReceiveNext(); // 변수1
changedVec = (Vector3)stream.ReceiveNext(); // 변수2
}
}
함수 동기화: PunRPC
함수는 기본적으로 동기화가 안된다.
동기화하듯이 모든 클라이언트가 영향을 받도록 함수를 호출해주려면 서버에서 호출하면 된다.
클라이언트에서 서버의 함수를 호출하는 것을 Remote Procedure Calls, 줄여서 RPC라고 부른다.
주로 이벤트 발생 시에 사용한다.
서버에서 호출하고 싶은 함수 위에
[PunRPC]
라는 키워드를 붙인다.
[PunRPC]
void RemoteFunction(string param1, string param2)
{
}
호출할 때는 PhotonView.RPC()
메서드를 사용한다.
if (photonView.IsMine)
photonView.RPC("RemoteFunction", RpcTarget.All, "param1", "param2");
RPC()의 두번째 파라미터는 어느 클라이언트들에게 함수 실행을 전달해줄지에 대한 타겟을 정하는 부분이다.
이 타겟설정에 따라 실행 결과가 굉장히 많이 바뀐다.
타겟종류를 정리하자면 다음과 같다.
All | 자신의 클라이언트는 함수를 바로 실행하고 다른 클라이언트들에게 전달 로컬 클라이언트가 리모트 클라이언트보다 빠르게 실행됨 |
AllViaServer | 모두가 서버를 거쳐서 원격 함수 호출을 전달받는다. 로컬과 리모트의 함수 실행 타이밍이 동일하다. |
AllBuffered | All은 호출하는 시기에 통신을 수행하고 바로 사라진다. 따라서 방에 나중에 들어온 사람들은 그 결과를 받지 못한다. AllBuffered는 원격 호출을 버퍼에 저장해두어 나중에 방에 들어온 사람들도 그 결과를 받아볼 수 있다. |
AllBufferedViaServer | 원격 호출 버퍼링 & 모두가 서버를 거쳐서 호출 통신 |
MasterClient | 마스터 클라이언트 (방장) 에게만 전달 |
Others | 나를 제외한 모두에게 전달 |
OtherBuffered | 나를 제외한 모두에게 버퍼링을 거쳐 전달 |
지금까지 설명한 것들을 조합하여 동기화를 구현할 수 있을 것이다.
RaiseEvent라는 것도 있는데, 이것은 PhotonView를 거치지 않는 이벤트이다.
많이 남발하면 느려질 위험이 있어 잘 쓰지 않는다길래, 여기에는 따로 정리하지 않았다.
포톤을 통한 동기화가 여기저기 많이 필요할 것 같은데, 막상 쉽게 정리된 정보들은 여기저기 흩어져 있어 한 번 모아서 정리해보았다.
포톤 PUN2를 쓸 때마다 두고두고 볼 예정이다.
Uploaded by N2T
'개발 > Unity 내일배움캠프 TIL' 카테고리의 다른 글
최종프로젝트를 앞두며 (0) | 2023.10.20 |
---|---|
Unity 포톤 동기화 작업 시 주의할 점들 (1) | 2023.10.19 |
Unity 포톤으로 멀티플레이 구현해보기 (0) | 2023.10.17 |
Unity2D 사이드뷰 게임의 좋은 움직임 만들기 (1) | 2023.10.16 |
Visual Studio 단축키 (0) | 2023.10.13 |