멀티플레이어 프로젝트 처음부터 시작
오브젝트 스포닝

네트워크 관리자 사용(Using the Network Manager)

NetworkManager 는 멀티플레이어 게임의 네트워크 상태를 관리하기 위한 컴포넌트입니다. 실제로 전부 고수준 API(HLAPI)를 사용하여 구현되어 있기 때문에, 네트워크 관리자가 하는 일은 다른 형태로도 할 수 있습니다. 하지만 Network Manager 컴포넌트는 유용한 여러 기능을 한 곳에 모아 멀티플레이어 게임의 제작, 실행, 디버깅을 최대한 간단히 할 수 있도록 해 줍니다.

네트워크 관리자는 스크립팅 없이도 전부 사용할 수 있습니다. 에디터에 인스펙터 컨트롤이 있어서 모든 기능 설정을 할 수 있습니다. 네트워크 관리자 HUD는 사용자가 네트워크 게임을 제어할 수 있도록 하는 디폴트 사용자 인터페이스를 런타임 도중 제공합니다. 고급 사용자의 경우, 개발자는 NetworkManager에서 클래스를 유도하여 제공되는 가상 함수 후크를 오버라이드하는 방식으로 동작을 커스텀화할 수 있습니다.

아래는 몇 가지 네트워크 관리자 기능입니다.

  • 게임 상태 관리
  • 스포닝 관리
  • 씬 관리
  • 디버깅 정보
  • 매치메이킹
  • 커스텀화

네트워크 관리자(Network Manager) 시작

네트워크 관리자는 멀티플레이어 게임의 핵심 제어 컴포넌트로 사용할 수 있습니다. 시작하려면 시작 씬에 빈 게임 오브젝트를 하나 생성하거나 Network Manager 컴포넌트를 호스트하기 편한 게임 오브젝트를 선택해야 합니다. 그 다음 Network/NetworkManager 메뉴 항목에서 NetworkManager 컴포넌트를 추가해야 합니다. 새로 추가한 NetworkManager 컴포넌트는 아래와 같을 것입니다.

에디터의 네트워크 관리자 인스펙터를 통해 네트워킹에 관련된 여러 사항을 설정하고 제어할 수 있습니다.

네트워크 관리자 HUD는 네트워크 관리자와 같이 작동하는 또 다른 컴포넌트입니다. 게임이 실행되는 동안 네트워크 상태를 제어할 수 있도록 단순한 사용자 인터페이스를 제공합니다. 이 컴포넌트는 네트워크 프로젝트를 시작하는 방법으로는 유용하지만, 게임의 최종 UI로 사용되기 위한 것은 아닙니다. NetworkManagerHUD 컴포넌트는 아래와 같습니다.

완성된 게임은 게임 상태를 제어하고 플레이어가 어떤 종류의 게임을 플레이할지 선택할 수 있도록 적절한 사용자 인터페이스를 필요로 합니다. 네트워크 관리자 HUD는 개발 목적으로만 사용해야 합니다.

게임 상태 관리

네트워킹 멀티플레이어 게임은 클라이언트, 전용 서버, 또는 동시에 클라이언트이자 서버인 “호스트”, 이렇게 세 가지 모드에서 실행할 수 있습니다. 네트워킹은 모든 경우에서 동일한 게임 코드와 에셋이 작동할 수 있도록 의도되었습니다. 따라서 게임의 싱글플레이어와 멀티플레이어 버전 개발 과정은 동일할 것입니다.

NetworkManager has funcions for entering each of these modes:

  • NetworkManager.StartClient()
  • NetworkManager.StartServer()
  • NetworkManager.StartHost()

이를 전부 스크립트 코드에서 사용할 수 있으므로 키보드 입력 핸들러나 커스텀 사용자 인터페이스에서 호출할 수 있습니다. 선택적으로 표시할 수 있는 디폴트 런타임 컨트롤 역시 동일하게 이들 함수를 호출합니다. 플레이 모드 중 에디터에서 사용할 수 있는 네트워크 관리자 HUD 인스펙터에서 버튼을 통해서도 동일한 함수를 호출할 수 있습니다.

함수를 사용하여 게임 상태를 변경한 경우networkAddressnetworkPort 프로퍼티가 사용됩니다. 서버나 호스트가 시작되면 networkPort는 수신 포트가 됩니다. 클라이언트가 시작되면 networkAddress는 연결할 주소가 되며, networkPort는 연결할 포트가 됩니다.

스폰 관리

네트워크 관리자를 프리팹에서 네트워크 게임 오브젝트 스포닝을 관리하는 데 사용할 수도 있습니다. 대부분의 게임은 메인 플레이어 게임 오브젝트로 프리팹을 사용하므로, 네트워크 관리자는 플레이어 프리팹을 드래그할 수 있도록 슬롯을 가지고 있습니다. 플레이어 프리팹이 설정된 경우 플레이어 게임 오브젝트는 자동으로 게임의 각 사용자 프리팹에서 자동으로 스폰됩니다. 이는 호스트된 서버에서의 로컬 플레이어와 원격 클라이언트에서의 원격 플레이어에 적용됩니다. 플레이어 프리팹에는 Network Identity 컴포넌트가 있어야 함에 유의해야 합니다.

플레이어 프리팹과 더불어 동적으로 스폰되는 다른 게임 오브젝트의 프리팹 역시 클라이언트 씬에 등록해야 합니다. 이는 ClientScene.RegisterPrefab() 함수를 사용하거나 네트워크 관리자가 자동으로 수행할 수 있습니다. 스폰 리스트에 프리팹을 추가하면 자동으로 등록됩니다. 네트워크 관리자 인스펙터의 스폰 설정 섹션은 아래와 같습니다.

플레이어 프리팹이 설정된 이후에는 게임을 호스트로 시작하면 플레이어 게임 오브젝트가 스폰되는 것을 볼 수 있습니다. 게임을 정지하면 플레이어 게임 오브젝트가 제거됩니다. 게임의 다른 사본을 실행하고 로컬 호스트에 클라이언트로 연결하면 다른 플레이어 게임 오브젝트가 나타날 것이며, 해당 클라이언트를 정지하면 클라이언트의 플레이어 게임 오브젝트가 제거됩니다.

플레이어 게임 오브젝트는 NetworkManager.OnServerAddPlayer의 직접 구현을 통해 스폰됩니다. 플레이어 게임 오브젝트가 생성되는 방법을 커스텀화하고 싶은 경우, 해당 가상 함수를 오버라이드하면 됩니다. 아래는 디폴트 구현의 예제 코드입니다.

public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
    var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}

플레이어 게임 오브젝트가 새로 생성된 경우 NetworkServer.AddPlayerForConnection() 함수가 호출되어야만 스폰되어 클라이언트 연결에 부착될 수 있습니다. 이 과정을 통해 게임 오브젝트가 스폰되므로 NetworkServer.Spawn는 플레이어 게임 오브젝트에 대해 호출되지 않아도 됩니다.

시작(Start) 포지션

플레이어가 스폰되는 위치를 제어하려면 Network Start Position 컴포넌트를 사용하면 됩니다. 네트워크 관리자는 Network Start Position 컴포넌트가 부착된 게임 오브젝트를 씬에서 찾으며, 만일 찾은 경우 플레이어를 그 중 하나의 위치와 방향에 스폰합니다. 커스텀 코드를 사용해서 NetworkManager.startPositions 리스트에서 사용할 수 있는 Network Start Position 컴포넌트에 접근하거나, 네트워크 관리자의 GetStartPosition() 헬퍼 함수를 통해 시작 위치를 찾을 수 있도록 OnServerAddPlayer를 구현하는 데 사용할 수 있습니다.

시작 위치를 사용하려면 씬 게임 오브젝트에 Network Start Position 컴포넌트를 부착해야 합니다. 씬 안에는 여러 개의 시작 포지션이 있을 수 있습니다. 그 후 네트워크 관리자는 게임 오브젝트의 포지션과 방향을 시작 포지션으로 등록합니다. 클라이언트가 게임에 참여하고 플레이어가 추가되면, 플레이어 게임 오브젝트는 시작 포지션 중 하나에 동일한 포지션과 방향을 가진 채 생성됩니다.

NetworkManager 컴포넌트에는 startPositions가 선택되는 방법을 설정하는 PlayerSpawnMethod 프로퍼티가 있습니다.

  • __Random__를 선택하면 플레이어는 startPosition 옵션 중 임의로 선택된 곳에 스폰됩니다.
  • __Round Robin__를 선택하면 startPosition 옵션 리스트 순서대로 스폰됩니다.

스폰 영역 코드는 아래와 같습니다.

if (m_PlayerSpawnMethod == PlayerSpawnMethod.Random && s_StartPositions.Count > 0)
{
    // try to spawn at a random start location
    int index = Random.Range(0, s_StartPositions.Count);
    return s_StartPositions[index];
}
if (m_PlayerSpawnMethod == PlayerSpawnMethod.RoundRobin && s_StartPositions.Count > 0)
{
    if (s_StartPositionIndex >= s_StartPositions.Count)
    {
        s_StartPositionIndex = 0;
    }
​
    Transform startPos = s_StartPositions[s_StartPositionIndex];
    s_StartPositionIndex += 1;
    return startPos;
}

씬(Scene) 관리

대부분의 게임에는 하나 이상의 씬이 있습니다. 최소한 타이틀 화면이나 시작 메뉴 씬이 있을 것이며, 실제로 게임이 플레이되는 씬이 있기 때문입니다. 네트워크 관리자를 설정하면 멀티플레이어 게임에 맞는 방식으로 씬 상태와 전환을 자동으로 관리할 수 있습니다. NetworkManager 인스펙터에는 offlineSceneonlineScene, 이렇게 두 개의 슬롯이 있습니다. 이 슬롯에 씬 게임 오브젝트를 드래그하면 네트워크 씬 관리가 활성화됩니다.

서버나 호스트가 시작되면 온라인 씬이 로드됩니다. 이 씬이 현재 네트워크 씬이 됩니다. 해당 서버에 연결하는 모든 클라이언트 역시 해당 씬을 로드하도록 지시를 받습니다. 이 씬의 이름은 networkSceneName 프로퍼티에 저장됩니다.

서버나 호스트가 정지되거나 클라이언트가 연결 해제되어 네트워크가 중단되면 오프라인 씬이 로드됩니다. 이를 통해 멀티플레이어 게임에서 연결이 해제되면 자동으로 메뉴 씬으로 돌아가도록 할 수 있습니다.

또한 NetworkManager.ServerChangeScene()를 호출하여 게임이 활성화된 경우에 씬을 변경할 수 있습니다. 이는 현재 연결된 클라이언트 역시 씬을 변경하게 되며, networkSceneName를 업데이트하여 새로운 클라이언트 역시 새로운 씬을 로드하도록 할 수 있습니다.

네트워크 씬 관리가 활성화된 경우 NetworkManager.StartHost()NetworkManager.StopClient()와 같은 게임 상태 관리 함수 호출은 씬 변경을 발생시킵니다. 런타임 제어 UI에도 적용됩니다. 씬을 설정하고 이들 함수를 호출하는 방법으로 멀티플레이어 게임의 흐름을 쉽게 제어할 수 있습니다.

씬 변경이 발생하면 이전 씬의 모든 게임 오브젝트가 제거된다는 점에 유의해야 합니다. 네트워크 관리자는 보통 씬이 바뀌더라도 유지되어야 합니다. 그렇지 않으면 씬이 변경되면 네트워크 연결이 해제될 것이기 때문입니다. 따라서 인스펙터에서 Don’t Destroy On Load 상자를 클릭해야 합니다. 각 씬의 네트워크 관리자가 다른 설정을 가지도록 할 수도 있으며, 이는 인크리먼트 프리팹 로딩이나 다른 씬 전환 방법을 사용하고자 할 때 유용합니다.

디버깅(Debugging) 정보

Network Manager HUD 인스펙터 창은 런타임 시점의 네트워크 상태에 대한 추가 정보를 제공합니다. 다음은 제공되는 몇 가지 정보입니다.

  • 네트워크 연결
  • 활성 NetworkIdentity 서버 오브젝트
  • 활성 NetworkIdentity 클라이언트 오브젝트
  • 클라이언트 피어

등록된 클라이언트 메시지 핸들러도 미리보기 창에 나타납니다.

매치메이킹

네트워크 관리자 런타임 UI와 네트워크 관리자 인스펙터 UI는 매치메이커 서비스와의 상호작용이 가능하도록 합니다. NetworkManager.StartMatchmaker() 함수는 매치메이킹을 활성화하며 NetworkManager.matchmaker 프로퍼티에 NetworkMatch 오브젝트를 제공합니다. 활성화 된 이후에는 디폴트 UI는 이를 사용하게 되며 NetworkManager 콜백 함수는 단순한 매치메이킹을 할 수 있도록 합니다.

NetworkManager에는 몇 가지 가상 함수가 있으며, 파생 클래스는 이를 사용하여 매치메이커 콜백에 반응하는 동작을 커스텀화할 수 있습니다.

커스텀화

NetworkManager에는 몇 가지 가상 함수가 있으며, 파생 클래스는 이를 사용하여 동작을 커스텀화할 수 있습니다. 이들 함수를 구현하는 경우 디폴트 구현 방식이 제공하는 기능을 잘 확인해야 합니다. 예를 들어, OnServerAddPlayer()의 경우 NetworkServer.AddPlayer 함수가 호출되어야만 플레이어 게임 오브젝트가 연결할 수 있도록 활성화됩니다.

아래는 서버나 호스트에서 호출되는 함수입니다.

// called when a client connects 
public virtual void OnServerConnect(NetworkConnection conn);

// called when a client disconnects
public virtual void OnServerDisconnect(NetworkConnection conn)
{
    NetworkServer.DestroyPlayersForConnection(conn);
}

// called when a client is ready
public virtual void OnServerReady(NetworkConnection conn)
{
    NetworkServer.SetClientReady(conn);
}

// called when a new player is added for a client
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
    var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}

// called when a player is removed for a client
public virtual void OnServerRemovePlayer(NetworkConnection conn, short playerControllerId)
{
    PlayerController player;
    if (conn.GetPlayer(playerControllerId, out player))
    {
        if (player.NetworkIdentity != null && player.NetworkIdentity.gameObject != null)
            NetworkServer.Destroy(player.NetworkIdentity.gameObject);
    }
}

// called when a network error occurs
public virtual void OnServerError(NetworkConnection conn, int errorCode);

아래는 클라이언트에서 호출되는 함수입니다.

// called when connected to a server
public virtual void OnClientConnect(NetworkConnection conn)
{
    ClientScene.Ready(conn);
    ClientScene.AddPlayer(0);
}

// called when disconnected from a server
public virtual void OnClientDisconnect(NetworkConnection conn)
{
    StopClient();
}

// called when a network error occurs
public virtual void OnClientError(NetworkConnection conn, int errorCode);

// called when told to be not-ready by a server
public virtual void OnClientNotReady(NetworkConnection conn);

아래는 매치메이커에서 호출되는 함수입니다.

// called when a match is created
public virtual void OnMatchCreate(CreateMatchResponse matchInfo)

// called when a list of matches is received
public virtual void OnMatchList(ListMatchResponse matchList)

// called when a match is joined
public void OnMatchJoined(JoinMatchResponse matchInfo)
멀티플레이어 프로젝트 처음부터 시작
오브젝트 스포닝