This document is about: PUN 2
SWITCH TO

PUN Classic (v1), PUN 2, Bolt는 휴업 모드입니다. Unity2022에 대해서는 PUN 2에서 서포트하지만, 신기능의 추가는 없습니다. 현재 이용중인 고객님의 PUN 및 Bolt 프로젝트는 중단되지 않고, 퍼포먼스나 성능이 떨어지는 일도 없습니다. 앞으로의 새로운 프로젝트에는 Photon Fusion 또는 Quantum을 사용해 주십시오.

Asteroids 데모

 

uNet 에서 PUN 으로 포팅하기

이 페이지에서는 유니티의 NetworkMeteoroid 데모를 uNet에서 PUN으로 포팅하는 과정을 설명합니다. 이 유니티 데모는 에셋 스토어의 이 링크에서 찾아 보실 수 있습니다. 이 페이지에서는 좀 더 멋진 결과를 얻기 위해 처리하기 쉬운 절차와 좀 더 다루기 복잡한 절차들에 대해 설명합니다. 따라서 이 페이지는 필요한 모든 단계와 중요한 단계를 다루기 위해 여러 섹션으로 나뉩니다. 단계가 올라 갈 수록 난이도는 높아집니다. 하지만 게임을 성공적으로 포팅하기 위해서 이 순서를 따라야 할 필요는 없습니다.

screenshot of the ported asteroids demo
포팅된 Asteroids 데모의 스크린 샷

또한 다음 설명은 기존 어플리케이션을 uNet에서 PUN으로 포팅하는 데 사용할 수 있는 일반적인 방법은 아니지만, 어떤 단계가 필요하고 어떤 문제를 처리해야 하는지에 대한 정보를 제공하고 있습니다.

 

에셋 다시 불러 들이기, 프리팹과 씬 빌드 다시하기

기존 프로젝트를 uNet에서 PUN으로 포팅할 때 기본적으로 기존 프로젝트에서 사용된 모든 uNET의 네트워크 로직을 PUN 로직으로 바꿀 수 있습니다. 익숙하지 않은 데모 작업을 하고 있기 때문에 새로운 프로젝트부터 시작해서 데모의 거의 대부분을 다시 작성하기로 했습니다. 이렇게 하면 처음부터 전체 포팅 프로세스에 대해서 이해 할 수 있습니다. 사실 그렇게까지 할 필요는 없지만, 이러한 방법을 통해 원래 데모와 포팅된 데모 사이의 모든 차이점을 최소한 소스 코드에서 확인할 수 있었습니다. 이렇게하는 것이 기존의 NetworkMeteoroid 데모를 사용하는 것보다 훨씬 더 많은 작업이 필요할 것 이라고 생각하는 것이 맞을수도 있을지 모르지만, 그렇게 하지 않으면 우리는 사소한 작업을 특히 많이 해야 할 것입니다. 따라서 이 데모를 위해 포팅 프로세스를 수행할 수 있는 두 가지 방법 사이의 작업 부하는 더 많거나 적을 수 있습니다. 프로젝트의 복잡성과 관련하여 이 경험은 분명히 바뀔 수 있습니다.

시작하기 위해 Unity에서 새 프로젝트를 만들고 원래 데모 폴더 구조를 재생성하고 필요한 에셋(모델 및 텍스처)을 다시 가져옵니다. 이 작업이 완료된 후 머터리얼, 프리팹 및 씬과 같은 다른 필수 에셋을 재구축하기 시작했습니다. 특히 ‘로비’씬을 재구성할 때 나중에 필요하지 않은 부분을 재생성하지 않도록 했습니다(예: 전용 서버 옵션). 또한 외관과 느낌이 PUN의 데모 허브에 맞도록 했습니다.

 

게임 로직에 세부 조정 적용하기

어플리케이션의 소스 코드에 수정을 적용할 때 시작할 지점이 필요합니다. 이 경우에는 할 일은 많이 없기 때문에 NetworkMeteoroid 데모의 게임 로직으로 시작하기로 결정했습니다. 물론 전체 코드를 일대일로 재사용할 수는 없지만, 데모 소스 코드를 패턴으로 사용하여 수정 사항을 적용하기만 하면 됩니다. 따라서 전체 코드를 복사하여 나중에 수정하거나 처음부터 수정된 코드를 사용하여 다시 작성할 수 있습니다. 이 데모에서는 두 가지 방법을 모두 사용했습니다. 결국 게임 로직 자체는 네트워크 관련 소스 코드에 특히 적용되는 일부 사소한 변경 사항 외에 원래 데모에서와 거의 동일합니다.

여기 하나의 예가 원래 데모에서 소행성이 어떻게 생성되는지를 보여줍니다. 당사의 수정 버전에서는 기본적으로 동일한 방식으로 작동합니다(게임 관리자 및 게임이 실행되는 동안 실행되는 코루틴 사용). 이 예제에서는 uNet의 인스턴스화 NetworkServer.Spawn(...) 를 간단하게 변경하였습니다. PUN의 씬 객체 인스턴스화를 PhotonNetwork.InstantiateSceneObject(...) 를 호출 합니다. InstantiationData 추가를 위해 이 호출을 소행성의 강체(RidentiationData)에 대한 추가 정보를 공유할 수 있습니다. 또한 이러한 종류의 정보를 동기화하기 위해 별도의 RPC 또는 RaiseEvent 호출을 사용할 필요가 없다는 잇점도 있습니다.

하지만 소스 코드에는 전혀 수정되지 않는 부분도 있습니다. 예를 들어 플레이어 입력을 처리하는 방법은 이미 잘 작동하고 있으므로 수정하지 않아도 되기 때문에 원래 데모와 정확하게 동일합니다.

 

네트워크 로직에 세부 조정 적용하기

이 부분에서는 uNet의 모든 네트워크 로직을 PUN 로직으로 대체하기 위해 데모 소스 코드에 몇 가지 중요한 수정 사항 적용은 (마침내) 매우 흥미롭습니다. 알림: 안타깝게도 기존 어플리케이션을 uNet에서 PUN으로 포팅할 때 기준이 되는 일반적인 방법이 없습니다. 따라서 네트워크 로직의 어떤 것을 처리할 수 있는 다른 방법이 있거나 속성 자체가 PUN에 전혀 없기 때문에 일반적으로 특정 uNET 속성(예: [ClientRpc])을 항상 특정 PUN 속성(예: [PunRPC])에 매핑할 수 있다고 말 할 수는 없습니다. 즉, 모든 코드 라인에 대해 네트워크와 관련된 소스 코드의 세그먼트에 적용할 수정 사항을 고려해야 합니다.

이 데모에서는 서버측 로직을 사용하지 않으므로 시뮬레이션은 원래 데모에서 서버가 제어하므로 처리 방법에 대한 또 다른 중요한 결정을 내려야 했습니다. 사용자 지정 서버측 로직 없이 PUN을 사용할 경우 시뮬레이션을 처리하기 위해 모든 클라이언트 또는 하나의 클라이언트를 사용할 수 있습니다. 우리의 경우에서는 두 번째 옵션을 선택하고 MasterClient를 사용하여 시뮬레이션을 실행하고 제어하기로 결정했습니다. 이는 소행성을 인스턴스화할 수 있는 유일한 클라이언트이며 또한 플레이어의 우주선에 의해 발사된 총탄으로 충돌 탐지를 처리한다는 것을 의미합니다. 게다가 이러한 소행성은 게임이 실행되는 동안 MasterClient가 연결을 끊어도 파괴되지 않는다는 잇점을 가진 씬 객체로 인스턴스화됩니다. 대신 이 역할을 대신할 수 있는 다른 클라이언트가 있는 한 시뮬레이션의 제어 권한은 새 MasterClient로 전달됩니다.

네트워크 로직의 또 다른 중요한 측면은 앞서 언급한 소행성과 플레이어 우주선의 동기화입니다. 납득할 만한 결과를 얻기 위해 필요한 모든 데이터의 송수신 작업을 처리하는 맞춤형 OnPhotonSerializeView 기능을 구현하기로 했습니다. 여기에는 Rigidbody 위치, 회전 및 속도가 포함됩니다. 추가 수정 사항이 적용된 이 사용자 지정 솔루션은 나중에 새로운 PhotonRigidbodyView 컴포넌트로 전환되었습니다.

 

지연보상을 추가하여 동기화 문제 해결하기

시뮬레이션을 설정하여 여러 클라이언트에서 실행한 후 동기화 문제가 눈에 띄게 발생하여 두 개 이상의 게임 창을 서로 나란히 실행해 보면 시각적으로 실망스러운 결과를 얻었습니다. 예를 들어, 두 개의 다른 화면에서 우주선의 위치가 이동하는 것입니다. 이것은 지연으로 인해 발생했고 전체 동기화에서 더 많은 문제를 일으켰습니다. 어떤 경우에는 플레이어의 우주선이 한 클라이언트의 관점에서는 소행성과 충돌했지만 다른 클라이언트의 관점에서는 충돌하지 않았습니다. 또한 MasterClient가 다른 클라이언트에서 전혀 보이지 않는 충돌을 감지했기 때문에 MasterClient가 때때로 다른 플레이어의 우주선을 폭발시킬 수 밖에 없었습니다. 그러한 이슈들은 모든 멀티플레이어 게임의 게임 플레이에는 치명적입니다.

이러한 동기화 문제를 제거하기 위해 우리는 소행성, 우주선 그리고 발사된 탄환에 지연 보상을 추가하기로 결정했습니다. 이 사례에서 지연 보상은 동기화된 객체의 정보를 수신하는 클라이언트가 이전에 수신한 정보의 도움을 받아 보다 정확하고 최신 데이터를 계산하려고 하는 것을 의미합니다. 예: 클라이언트가 다른 우주선의 정보를 수신할 때마다 수신된 위치 및 속도 값과 메시지의 타임스탬프 및 현재 타임스탬프를 사용하여 다른 우주선의 최신 위치를 계산합니다. 이 보다 정확한 위치를 계산한 결과, 우리는 유니티의 FixedUpdate 함수를 이용하여 우주선을 ‘실제’ 위치로 한 단계 더 가까이 이동시키고 있습니다. 적어도 이것이 물체의 ‘실제’ 위치라고 생각하는 위치까지 말입니다. 앞서 설명한 기능의 구현을 보여 주는 아래의 코드 부분들을 확인할 수 있습니다.

C#

public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
   if (stream.IsWriting)
   {
       stream.SendNext(rigidbody.position);
       stream.SendNext(rigidbody.rotation);
       stream.SendNext(rigidbody.velocity);
   }
   else
   {
       networkPosition = (Vector3)stream.ReceiveNext();
       networkRotation = (Quaternion)stream.ReceiveNext();
       rigidbody.velocity = (Vector3)stream.ReceiveNext();

       float lag = Mathf.Abs((float)(PhotonNetwork.Time - info.timestamp));
       networkPosition += (rigidbody.velocity * lag);
   }
}

소유자는 우주선의 위치, 회전 그리고 속도 같은 중요한 정보만 보냅니다. 수신자는 이 정보를 사용하여 로컬에 저장된 값을 업데이트하고 위치에 지연 보정을 적용합니다...

C#

public void FixedUpdate()
{
   if (!photonView.IsMine)
   {
       rigidbody.position = Vector3.MoveTowards(rigidbody.position, networkPosition, Time.fixedDeltaTime);
       rigidbody.rotation = Quaternion.RotateTowards(rigidbody.rotation, networkRotation, Time.fixedDeltaTime * 100.0f);
   }
}

...원격 객체가 FixedUpdate 함수에서 로컬로 저장된 데이터로 업데이트되기 전에 선택합니다.

참고: 위에 표시된 코드 부분이 PUN 패키지의 실제 PhotonRidentbodyView 컴포넌트를 나타내지 않습니다.

결국 포팅된 데모에 지연 보상을 추가하여 훨씬 더 나아졌습니다. 여기에는 설득력 있는 시각적 표현과 향상된 네트워크 동기화를 통해 전반적인 만족감을 주는 게임 플레이가 포함됩니다.

screenshot of the ported asteroids demo
포팅된 Asteroids 데모의 또 다른 스크린 샷
Back to top