레트로의 유니티6 : NGO 네트워크 게임 PONG - 2

심지태
∙∙조회 8
레트로의 유니티6 : NGO 네트워크 게임 PONG - 2

네트워크 스폰

네트워크 오브젝트 컴포넌트를 가진 게임 오브젝트는 네트워크 스폰할 수 있다. 즉, 어떤 클라이언트가 네트워크 오브젝트를 가진 게임 오브젝트를 생성했을 때, 다른 클라이언트에서도 동일한 게임 오브젝트가 생성되도록 할 수 있다.

ini
// 예시 코드
var instance = Instantiate(prefab);
var networkObject = instance.GetComponent<NetworkObject>();
networkObject.spawn();

단, Spawn() 메서드는 보안을 위해 서버(호스트)에서만 실행된다. 만일 서버가 아닌 클라이언트가 네트워크 스폰을 해야 한다면, RPC를 통해 클라이언트가 서버에게 해당 게임 오브젝트에 대한 네트워크 스폰을 대신 실행하도록 해야함.

네트워크 스폰이 동작하는 방식

  1. 게임 오브젝트/프리팹에 네트워크 오브젝트 컴포넌트 추가
  2. 해당 네트워크 오브젝트 컴포넌트는 글로벌 오브젝트 ID 해시(GlobalObjectIdHash) 값을 가지게 됨. 글로벌 오브젝트 ID 해시는 네트워크 스폰을 할 때 각 클라이언트가 생성할 프리팹을 찾는 데 사용됨.

→ Spawn() 메서드를 통해 네트워크 스폰을 실행하면 각 클라이언트는 네트워크 매니저에 등록된 네트워크 프리팹들 중에서 같은 글로벌 오브젝트 ID 해시를 가진 프리팹을 찾아 인스턴스화함. 이는 게임 도중에 네트워크 스폰할 모든 프리팹들이 미리 네트워크 매니저에 등록되어야 하는 이유이기도함.

 

소유권

  
IsOwner로컬 클라이언트의 소유인가, 또는 로컬 플레이어 오브젝트인가
IsOwnedByServer서버가 소유한 오브젝트인가
IsSpawned게임 도중 스폰된 것인가
IsSceneObejct씬에 처음부터 있던 오브젝트인가
DestroyWithScene씬과 함께 파괴할 오브젝트인가
IsPlayerObejct플레이어의 아바타인가
IsLocalPlayer로컬 플레이어의 아바타인가

IsOwner를 통해 어떤 네트워크 오브젝트가 내(로컬 클라이언트)가 소유한 오브젝트인지 알 수 있다.

기본적으로 다음과 같이 먼저 원본 게임 오브젝트를 인스턴스화하고 네트워크 스폰을 실행한 측에서 해당 네트워크 오브젝트를 소유한다.

ini
// 이 코드는 보통 호스트 또는 서버에서 실행, 기본 소유자: 서버(또는 호스트)
var instance = Instantiate(prefab);
var networkObject = instance.GetComponent<NetworkObject>();
networkObject.Spawn();

다만, 다음과 같은 코드들을 통해 서버가 네트워크 오브젝트의 소유권을 다른 클라이언트에게 이전해줄 수 있다.

  • 특정 클라이언트를 소유자로 지정하면서 네트워크 스폰
ini
var instance = Instantiate(prefab);
var networkObject = instance.GetComponent<NetworkObject>();
networkObject.SpawnWithOwnership(clientId);
  • 이미 생성된 네트워크 오브젝트의 소유자를 변경
ini
var instance = Instantiate(prefab);
var networkObject = instance.GetComponent<NetworkObject>();
networkObject.Spawn(); // 먼저 네트워크에 스폰
networkObject.ChangeOwnership(clientId);

또한 네트워크 스폰을 할 때 SpawnAsPlayerObject() 메서드를 통해 네트워크 오브젝트가 어떤 플레이어의 아바타로서 생성되도록 지정할 수도 있다.

ini
/*
즉, 단순히 소유권만 주는 것이 아니라:
해당 클라이언트의 플레이어 오브젝트로 지정
해당 클라이언트가 소유
IsPlayerObject가 true가 됨
*/
var instance = Instantiate(prefab);
var networkObject = playerGameObject.GetComponent<NetworkObject>();
networkObject.SpawnAsPlayerObject(clientId);

※ 소유권과 플레이어 오브젝트 정리

구분의미
소유권누가 이 오브젝트를 소유하고 제어하는가
플레이어 오브젝트해당 클라이언트의 대표 아바타인가

로비 구현

LobbyManager 클래스가 네트워크 이벤트를 감지하고 동기화 기능을 활용할 수 있도록 NetworkBehaviour 클래스를 상속시킨다.

NetworkBehaviour 타입은 유니티 네트워크 이벤트들을 감지할 수 있다. 또한 RPC와 NetworkVariable 타입 등 넷코드 관련 네트워크 기능을 사용할 수 있다.

csharp
// OnNetworkSpawn은 NetworkBehaviour를 상속한 스크립트에서 자동 실행되는 네트워크 이벤트 메서드
    // OnNetworkSpawn은 NetworkBehaviour가 스폰(생성)될 때 호출됨
    // 네트워크 오브젝트가 씬에 미리 배치되어 있다면, 해당 씬이 로드되고 NGO가 오브젝트를 네트워크 오브젝트로 초기화한 직후 OnNetworkSpawn()이 실행된다. -> 씬에 같은 오브젝트가 있어도, 각 컴퓨터에서 별도의 로컬 오브젝트로 초기화한 뒤 NGO가 서로 연결해 관리한다는 뜻
    // 효과는 호스트와 클라이언트가 각자 가진 오브젝트를 NGO가 같은 네트워크 오브젝트로 연결해 위치·상태 등을 동기화하고, 그 연결이 끝난 뒤 초기화 코드를 실행할 수 있게 하는 것
    public override void OnNetworkSpawn()
    {
        // 먼저 자기 자신의 준비 상태를 등록
        _clientReadyStates.Add(NetworkManager.LocalClientId, false);

        // 서버인 경우에만 네트워크 매니저에 클라이언트가 호스트에 연결되었거나(OnClientConnectedCallback), 연결이 끊어졌을 때(OnClientisconnectedCallback) 실행할 콜백을 등록한다.
        if (IsServer)
        {
            // 클라이언트가 접속하거나 떠났을 때 호출할 콜백 등록
            NetworkManager.OnClientConnectedCallback += OnClientConnected;
            NetworkManager.OnClientDisconnectCallback += OnClientDisconnected;

            // 클라이언트가 씬 로드를 완료했을 때 호출할 콜백 등록
            // 각 클라이언트가 네트워크 매니저를 통해 씬을 로드할 때마다 자동 실행되는 OnLoadComplete 이벤트에 OnClientSceneLoadComplete 콜백을 등록함.
            // OnLoadComplete 이벤트는 자신뿐만 아니라 다른 모든 클라이언트들이 씬을 로드할 때마다 호스트 측에서 실행됨.
            NetworkManager.SceneManager.OnLoadComplete += OnClientSceneLoadComplete;

            /*
            클라이언트가 새로 연결될 때 해당 클라이언트가 연결은 되었지만 로딩 등의 이유로 아직 로비 씬에 진입하지 않았을 수도 있다.
            OnClientConnectedCallback 이벤트만 감지하면 클라이언트가 연결된 것만 알 수 있고 로비 씬까지 진입했는지는 알 수 없다.
            네트워크 매니저의 OnClientConnectedCallback와 SceneManager.OnLoadComplete 이벤트를 모두 구독함으로써 각 클라이언트가 접속은 했으나 아직 로비 씬으로 진입하지 못한 상태와 연결도 되었고 로비 씬에 진입한 상태를 나누어 감지할 수 있다.
            */
        }

        UpdateLobbyText(); // 로비 텍스트를 갱신
    }

RPC 속성

속성호출하는 쪽실제 실행되는 쪽주된 용도
[ServerRpc]클라이언트서버 또는 호스트클라이언트의 요청을 서버에 전달
[ClientRpc]서버 또는 호스트모든 클라이언트서버의 결과나 명령을 클라이언트에 전달

메서드 명은 각 속성을 메서드명 끝에 달아준다.

예시: ClientRpc 속성 선언과 메서드 작성