본문 바로가기

게임 개발/유니티

[유니티] Photon Fusion 102 간단 요약

728x90

# Photon Fusion 102

Photon Fusion 102은 Photon 홈페이지의 기술문서에 있는 Fusion 튜토리얼 중 한 단계를 말합니다.

 

 

 

# 방만들기, 접속하기

Photon Fusion 네트워크의 주체는 <NetworkRunner> 입니다.

NetworkRunner의 멤버 함수인 StartGame(StartGameArgs args) 로

Fusion 네트워크를 시작할 수 있습니다.

private NetworkRunner _runner;	// 러너 변수를 선언하고
_runner = gameObject.AddComponent<NetworkRunner>(); // 객체를 생성합니다.
_runner.ProvideInput = true;	// 이 클라이언트에서 입력을 한다는 뜻

await _runner.StartGame(new StartGameArgs()
{
    GameMode = mode,	// 서버 혹은 클라이언트 등 
    SessionName = "TestRoom",	
    //Scene = SceneManager.GetActiveScene().buildIndex,
    //SceneManager = gameObject.AddComponent<NetworkSceneManagerDefault>()
});

'방 만들기' 버튼에는 Host mode로 

'방 들어가기' 버튼에는 Client mode로 StartGame을 호출하면 됩니다.

 

주석처리한 부분은 씬 관리를 위한 부분이나 Fusion 100에서 실질적으로 사용하지 않습니다.

주석처리하여도 실행하는데 문제는 없습니다.

 

 

 

# INetworkRunnerCallbacks 인터페이스

NetworkRunner가 C#스크립트와 상호작용하기 위해서 구현해야할 인터페이스입니다.

INetworkRunnerCallbacks 인터페이스에는 아래와 같이

다양한 상황에 호출되는 함수들이 있습니다.

  public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)    {    }
  public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)    {    }
  public void OnInput(NetworkRunner runner, NetworkInput input);
  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 OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken);
  public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment<byte> data) { }
  public void OnSceneLoadDone(NetworkRunner runner) { }
  public void OnSceneLoadStart(NetworkRunner runner) { }

 

NetworkRunner 객체가 게임을 시작하여 세션을 생성하였다면 처음에는 방을 만든 호스트가 입장하게 될 것입니다.

그 다음 클라이언트 하나가 게임을 시작해서 세션에 입장하게 될 것입니다.

이 처럼 세션에 플레이어가 입장할 때 호출되는 함수, 그리고 퇴장할 때 호출되는 함수는 다음과 같습니다.

public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)

입장할 때의 NetworkRunner 객체와 플레이어의 정보를 인수로 받아옵니다.

 

 

 

게임이 Host mode이기 때문에 호스트만 새 개체를 생성할 수 있도록 합니다.

 

// 입장
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
	// 누군가가 입장하면 호스트를 담당하는 runner가 플레이어의 아바타 생성을 담당합니다.
    if (runner.IsServer)
    {
        Vector3 spawnPosition = new Vector3((player.RawEncoded % runner.Config.Simulation.DefaultPlayers) * 3, 1, 0);
        
        // Instantiate()의 Fusion 버전으로 생각하면 됩니다.
        // 네트워크 오브젝트로 생성합니다.
        NetworkObject networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
        _spawnedCharacters.Add(player, networkPlayerObject);
    }
        
}

// 퇴장
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
    if(_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
    {
    	// Destroy()의 Fusion 버전
        runner.Despawn(networkObject);
        _spawnedCharacters.Remove(player);
    }
}

 

 

 

# 입력 수집

입력이 들어올 때 호출되는 INetworkRunnerCallbacks 의 함수는 다음과 같습니다.

public void OnInput(NetworkRunner runner, NetworkInput input);

인수로 NetworkRunner와 함께 NetworkInput이 들어오는데

NetworkInput이 동작하기 위해서 INetworkInput 인터페이스를 구현한 구조체가 필요합니다.

 

public struct NetworkInputData : INetworkInput // OnInput()을 위한 인터페이스입니다.
{
    public const byte MOUSEBUTTON1 = 0x01;
    public const byte MOUSEBUTTON2 = 0x02;
    public byte buttons;
    public Vector3 direction;
}

INetworkInput 인터페이스가 위에 정의된 NetworkInputData 구조체를

OnInput()의 인수로 사용하도록 해줍니다.

 

 

 

그리고 OnInput() 함수를 구현합니다.

public void OnInput(NetworkRunner runner, NetworkInput input)
{
    var data = new NetworkInputData();

    if (Input.GetKey(KeyCode.W)) data.direction += Vector3.forward;
    if (Input.GetKey(KeyCode.S)) data.direction += Vector3.back;
    if (Input.GetKey(KeyCode.A)) data.direction += Vector3.left;
    if (Input.GetKey(KeyCode.D)) data.direction += Vector3.right;
    if (_mouseButton0) data.buttons |= NetworkInputData.MOUSEBUTTON1;
    if (_mouseButton1) data.buttons |= NetworkInputData.MOUSEBUTTON2;
    _mouseButton0 = false;
    _mouseButton1 = false;

    input.Set(data);
}

input의 Set() 인수로 INetworkInput 을 구현한 NetWorkInputData 구조체 형태로 넘어갑니다.

 

 

 

# 입력 적용하기

각 아바타들이 정확하게 상호작용하기 위해서 각자의 스크립트에서 독립적으로 돌아가는 MonoBehaviour 대신

Fusion에서 제공하는 NetworkBehaviour 를 상속받아야 합니다.

NetworkBehaviourMonoBehaviour 를 상속받았습니다.

 

 

NetworkBehaviour를 상속받으면 FixedUpdateNetwork를 사용할 수 있습니다.

public class Player : NetworkBehaviour
{
  public override void FixedUpdateNetwork(){}
}

FixedUpdateNetwork는 MonoBehaviour의 update와 다르게

네트워크 상태가 적용된 시뮬레이션 틱에서 호출됩니다.

 

그리고 NetworkBehaviour는 위 틱에 대한 입력을 얻을 수 있는

GetInput() 메서드를 제공합니다.

 

마지막으로 NetworkCharacterControllerPrototype 컴포넌트를 통해서

플레이어의 아바타를 움직이면 됩니다.

public override void FixedUpdateNetwork()
{
  if (GetInput(out NetworkInputData data))
  {
    data.direction.Normalize();
    _cc.Move(5*data.direction*Runner.DeltaTime);
  }
}

 

 

728x90