본문 바로가기

23주차

유데미 스타터스 취업 부트캠프 1기 - 23주차 학습 일지

이번 주는 'Photon Fusion'에 대해서 공부했다.

 

< Fusion >

- Unity를 위한 새로운 고성능 상태 동기화 네트워킹 라이브러리

- 단일 API로 전용 서버, 클라이언트 호스팅 및 공유/분산 권한과 같은 다양한 네트워크 아키텍처 지원

- 공통 유니티 작업 흐름에 자연스럽게 통합되는 동시에 데이터 압축, 클라이언트 측 예측 및 지연 보상과 같은 고급 기능을 즉시 사용할 수 있도록 단순성을 염두에 두고 구축

- 내장된 조정을 통해 강력한 틱 기반 시뮬레이션 및 상태 스냅샷 시스템을 구현

- 각 클라이언트가 자신의 객체에 대한 전체 권한을 갖는 클라우드 기반 공유 모드 외에도 Fusion에는 두 개의 전체 서버 권한 모드가 포함되어 있음

1. 전용 헤드리스 유니티 인스턴스를 사용하여 엄격한 클라이언트/서버를 설정

2. 서버와 클라이언트(호스트)로 모두 작동하는 클라이언트 중 하나를 호스트로서 플레이

 

< PUN, Bolt, Fusion 비교 >

 

< Fusion 설정 하는 법 >

1. 필요 사양 확인 (Unity 2020.3.x LTS 이상)

2. Fusion SDK 받기 (https://doc.photonengine.com/ko-kr/fusion/current/getting-started/sdk-download)

3. 빈 프로젝트 생성

4. 에셋 직렬화 (Edit -> Project Settings -> Editor -> Asset Serialization -> Mode -> Force Text) 

5. Moco Cecil 설치

Window -> Package Manager -> Click the + icon > Add package from git URL로 이동하여 

com.unity.nuget.mono-cecil을 추가

6. App ID 생성 및 붙여넣기

 

 

< Fusion 실행하기 >

Fusion을 실행하기 위해서, StartGame 메소드가 Fusion NetworkRunner에서 호출되어야 하기 때문에 애플리케이션이 씬에서 이 컴포넌트를 반드시 가지고 있거나 코드에서 추가해야 한다.

 

다음은 필요한 스텁과 인터페이스이다.

public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks
{
  public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)    {    }
  public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)    {    }
  public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
  public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
  public void OnConnectedToServer(NetworkRunner runner) { }
  public void OnDisconnectedFromServer(NetworkRunner runner) { }
  public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
  public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
  public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
  public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) { }
  public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary<string, object> data) { }
  public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment<byte> data) { }
  public void OnSceneLoadDone(NetworkRunner runner) { }
  public void OnSceneLoadStart(NetworkRunner runner) { }
}

INetworkRunnerCallbacks 구현은 Fusions NetworkRunner BasicSpawner 클래스와 상호작용하는 것을 허용하게 된다. NetworkRunner는 Fusion의 중심이고 실제 네트워크 시뮬레이션을 수행한다.

 

NetworkRunner는 자동적으로 BasicSpawner INetworkRunnerCallbacks를 구현한지를 감지하고,

StartGame 메소드내의 동일한 게임 객체에 추가되기 때문에 메소드들을 호출한다.

async void StartGame(GameMode mode)
{
  // Create the Fusion runner and let it know that we will be providing user input
  _runner = gameObject.AddComponent<NetworkRunner>();
  _runner.ProvideInput = true;

  // Start or join (depends on gamemode) a session with a specific name
  await _runner.StartGame(new StartGameArgs()
  {
    GameMode = mode,
    SessionName = "TestRoom",
    Scene = SceneManager.GetActiveScene().buildIndex,
    SceneObjectProvider = gameObject.AddComponent<NetworkSceneManagerDefault>()
  });}

1,. StartGame 메소드는 먼저 Fusion NetworkRunner를 만들고 이 클라이언트가 입력을 제공할 것임을 알려줌

2. 하드 코딩된 이름과 지정된 게임 모드를 사용하여 새 세션을 시작.

3. 현재 씬 인덱스가 전달되지만 클라이언트가 호스트에서 지정한 씬을 사용하도록 강제되기 때문에 이는 호스트에만 관련

4. 양호한 측정을 위해 기본 SceneObjectProvider가 지정. SceneObjectProvider는 씬에 직접 배치된 NetworkObject의 인스턴스화를 처리

 

< 플레이어 설정 >

게임이 되기 위해서는 플레이어마다 입력을 제공하고 플레이어 아바타 등 세상에 존재감을 가질 수 있는 방법이 주어져야 한다.

 

Unity Editor에서,

  1. PlayerPrefab이라고 하는 빈 GameObject를 생성
  2. 여기에 NetworkObject 컴포넌트를 추가

이렇게 하면 모든 피어가 참조할 수 있도록 네트워크 ID를 제공한다.

사용자가 이 아바타를 제어할 것이므로 NetworkCharacterController도 필요하다.

요구 사항은 아니지만 대부분의 플레이어 제어 객체에 좋은 시작점이 될 수 있다.

일반적으로 네트워크 개체와 시각적 표현을 분리하고 시각적 표현이 부드럽게 보간되는 동안 네트워크 개체가 네트워크 상태로 스냅 되도록 하는 것이 좋다.

 

이를 달성하기 위해서

  1. PlayerPrefab의 자식 객체로 Cube를 추가한다.
  2. Body로 이름을 바꿔준다.
  3. Collider를 제거한다.
  4. Body를 드래그하여 부모 NetworkCharacterController의 Interpolation Target 속성에 놓는다.

마지막으로, 캐릭터 컨트롤러는 콜라이더가 필요하므로

  1. 추가적인 Cube 자식 객체를 추가한다.
  2. Collider로 이름을 바꿔준다.
  3. MeshRenderer 그리고 MeshFilter 컴포넌트를 제거한다.
  4. 이것을 NetworkCharacterController의 Collider 속성으로 드래그한다.

 

< 플레이어 스폰 >

게임이 호스트 모드로 실행되기 때문에 호스트만 새 개체를 생성할 수 있는 권한을 가진다.

즉, 모든 플레이어 아바타는 세션에 참여할 때 호스트에 의해 생성되어야 한다.

편리하게도 바로 이때 INetworkRunnerCallbacks 인터페이스의 OnPlayerJoined 메소드가 호출된다.

유사하게, 플레이어가 연결 해제할 때, OnPlayerLeft 메소드가 호출됩니다.

[SerializeField] private NetworkPrefabRef _playerPrefab;
private Dictionary<PlayerRef, NetworkObject> _spawnedCharacters = new Dictionary<PlayerRef, NetworkObject>();

public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
  // Create a unique position for the player
  Vector3 spawnPosition = new Vector3((player.RawEncoded%runner.Config.Simulation.DefaultPlayers)*3,1,0);
  NetworkObject networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
  // Keep track of the player avatars so we can remove it when they disconnect
  _spawnedCharacters.Add(player, networkPlayerObject);
}

public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
  // Find and remove the players avatar
  if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
  {
    runner.Despawn(networkObject);
    _spawnedCharacters.Remove(player);
  }
}

유니티의 Instantiate() 메소드를 마지막 매개 변수를 제외하고 유사한 매개 변수 집합을 사용하는 runner.Spawn()로 대체하기 때문에 이 모든 것이 익숙해 보일 것이다.

마지막 매개 변수는 아바타에 대한 입력을 제공할 수 있는 플레이어에 대한 참조이다.


유데미코리아 바로가기 : https://bit.ly/3b8JGeD

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

자료출처: https://www.photonengine.com/ko-KR/Photon