ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity] Unity Relay 를 통해서 P2P 연결 만들어보기
    쾌락없는 책임 (공부)/Unity 2023. 2. 1. 13:29
    반응형

    개요

    이전 소켓 프로그래밍으로 네트워크를 제작할 때 한가지 문제점을 보게 되었습니다. 일단 포트 포워딩 없이 다른 IP를 연결하기가 힘들었고 다른 방법이 있다 했지만 네트워크 관련 지식이 없어서 다음으로 미루기만 했습니다.

    다만 유니티의 최신 버전들 부터는 퍼스트 파티 네트워크 SDK 들이 생겼고 이후 Lobby와 함께 Relay를 활용해 게임 제작해 사용을 했습니다. 그래서 이번 글에서 Lobby에 이어 Relay를 간단하게 설명하려고 합니다.

     

     

    Lobby

    유니티 공식에서도 Lobby와의 조합이 좋다고 합니다. 물론 대규모를 처리하기 위한 데디케이트 서버가 아니지만 소규모 정도는 Lobby + Relay로 충분히 가능할겁니다.

    Lobby는 이름처럼 매치메이킹을 위한 SDK 입니다. 이와 관련한 내용을 이전에 써둔 적이 있으니 한번 참고하시면 됩니다.

     

    [Unity] 유니티 멀티플레이를 위한 Lobby, 플레이어 매치메이킹

    개요 친구와 함께 플젝을 시작하기 전, 최근 유니티에서 퍼스트파티로 네트워크 서비스를 제공하는 걸 접했습니다. Unity NetCode, Lobby, Relay들이 그 주인공들인데 최신 버전들은 이 혜택을 받을 수

    husk321.tistory.com

     

     

     

    Unity Game Service에서 활성화

     

    Unity Gaming Services

     

    dashboard.unity3d.com

    위 링크에서 Multiplayer > Relay를 먼저 활성화 하고 시작하셔야 합니다.

     

     

    Relay로 간단하게 참여하기

    일단 현재 제 프로젝트에 큰 기능이 필요한 것은 아니라서 릴레이를 만들고 참여하는 정도의 코드만 있습니다.

    public class RelaySingleton : MonoBehaviour
    {
        public static async Task<string> CreateRelay()
        {
            try
            {
                var playerCount = LobbySingleton.instance.GetJoinedLobby().MaxPlayers - 1;
                Allocation allocation = await RelayService.Instance.CreateAllocationAsync(playerCount);
    
                string joinCode = await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId);
    
                Debug.Log($"join code is : {joinCode}");
    
                NetworkManager.Singleton.GetComponent<UnityTransport>().SetHostRelayData(
                    allocation.RelayServer.IpV4,
                    (ushort)allocation.RelayServer.Port,
                    allocation.AllocationIdBytes,
                    allocation.Key,
                    allocation.ConnectionData
                );
    
                NetworkManager.Singleton.StartHost();
    
                return joinCode;
            }
            catch(RelayServiceException e)
            {
                Debug.Log($"{e}");
                return null;
            }
        }
    
        public static async void JoinRelay(string joinRelayCode)
        {
    
            try
            {
                JoinAllocation joinAllocation = await RelayService.Instance.JoinAllocationAsync(joinRelayCode);
    
                Debug.Log($"joined relay by : {joinRelayCode}");
    
                NetworkManager.Singleton.GetComponent<UnityTransport>().SetClientRelayData(
                    joinAllocation.RelayServer.IpV4,
                    (ushort)joinAllocation.RelayServer.Port,
                    joinAllocation.AllocationIdBytes,
                    joinAllocation.Key,
                    joinAllocation.ConnectionData,
                    joinAllocation.HostConnectionData
                );
    
                NetworkManager.Singleton.StartClient();
            }
            catch(RelayServiceException e)
            {
                Debug.Log($"{e}");
            }
        }
    }

    위 코드는 Unity Transport 

     

     

    Lobby와 조합해서 멀티 플레이 게임 시작하기

    위 릴레이를 만들고 참여하는 코드가 준비되었으니 플레이어를 모으는 Lobby에서 이 릴레이 코드를 활용해 보겠습니다. 일단 로비 주인(방장)이 게임을 시작하게 하는 Lobby 코드입니다.

    public async void StartGame()
    {
        if(!IsLobbyhost())  return;
    
        try
        {
        	// 릴레이 코드를 가져온다
            string relayCode = await RelaySingleton.CreateRelay();
    		// 본인만의 게임 시작 데이터를 변경해준다
            Lobby lobby = await Lobbies.Instance.UpdateLobbyAsync(joinedLobby.Id, new UpdateLobbyOptions{
                Data = new Dictionary<string, DataObject>{
                    { NetworkConstants.GAMESTART_KEY, new DataObject(DataObject.VisibilityOptions.Member, relayCode) }
                }
            });
    
            joinedLobby = lobby;
        }
        catch (LobbyServiceException e)
        {
            Debug.Log($"{e}");
        }
    }

    저 같은 경우 로비의 Data Dictionary에 게임 시작을 알리는 키가 있는데 이곳에 릴레이 코드를 넣어 줬습니다. 이후 방장이 이 함수를 실행시키면 로비에서 이 Data 정보가 변경될 것입니다.

    // Lobby 글 참고
    // 이 함수는 Update 에서 일정 주기로 불리고 있음
    public async void RefreshLobbyInfomation()
    {
        if(joinedLobby == null) return;
    
        lobbyInfomationUpdateTimer += Time.deltaTime;
        if(lobbyInfomationUpdateTimer < NetworkConstants.LOBBY_INFO_UPDATE_TIME)   return;
    
        lobbyInfomationUpdateTimer = 0.0f;
        var lobby = await LobbyService.Instance.GetLobbyAsync(joinedLobby.Id);
        joinedLobby = lobby;
        
        // 게임 시작 키가 디폴트가 아님! = 게임 시작한다는 뜻
        if(joinedLobby.Data[NetworkConstants.GAMESTART_KEY].Value != NetworkConstants.GAMESTART_KEY_DEFAULT)
        {
            if(!IsLobbyhost())
            {
                // 방장은 이미 릴레이를 만든 상태이니
                // 방장 제외 다른 플레이어들은 Dictionary의 데이터에서 릴레이 코드를 받아오면 된다
                RelaySingleton.JoinRelay(joinedLobby.Data[NetworkConstants.GAMESTART_KEY].Value);
            }
    
            joinedLobby = null;
    
            gameStartEvent?.Invoke();
            return;
        }
    	// .. 참가햇다면 return 이라 볼 일이 없다!
    }

    방장을 제외한 다른 플레이어들은 Update 에서 이를 감지해 로비 데이터의 Data.Dictionary에서 릴레이 코드를 가져옵니다. 이후 이를 통해서 참여만 하면 되는 것이죠

    반응형

    댓글

Designed by Tistory.