Tiny Star
본문 바로가기
Project/Unity Bootcamp 13th

[VR 숫자야구 게임] | Relay Server, Unity Service 의 Relay 이용하기

by 별터 2025. 8. 18.

이번 숫자야구 게임은 멀티플레이 게임이므로 서버와 클라이언트 스크립트를 나눠서 작성해야 한다.

 

멀티플레이를 만드는 방식은 여러 가지가 있는데 그 중 릴레이 서버를 이용해 이번 게임을 만들기로 했다.

 

1. Relay Server 작동 방식

1단계. 할당 요청

호스트 플레이어가 CreateAllocationAsync() 를 호출하면, 유니티의 릴레이 서비스는 게임을 위한 '전용 중계 서버(Allocation)'를 할당하고, 이 서버를 식별하는 고유한 ID 인 AllocationId 를 반환합니다.

이 AllocationId 는 릴레이 서버의 '주소'와 같은 역할을 합니다.

 

Allocation
: RelayService 에서 할당 받은 릴레이 서버 인스턴스를 나타내는 객체.

AllocationId, RelayServer, ConnectionData 가 포함되어 있다.

 

2단계. 초대 코드 생성

게스트 플레이어가 이 서버에 접속하려면 AllocationId 가 필요하지만, 이 ID 는 길고 복잡해서 사용자가 직접 입력하기 어렵다.

그래서 유니티는 GetJoinCodeAsync() 메서드를 제공합니다. 이 메서드는 AllocationId 를 짧고 간편한 JoinCode 로 변환해 줍니다.

 

3단계. 게스트 접속

게스트 플레이어는 이 JoinCode 를 사용해 서버에 접속 요청을 보냅니다. 유니티 릴레이 서비스는 JoinCode를 다시 AllocationId 로 변환하여 올바른 릴레이 서버로 게스트를 연결해 줍니다.

 

using UnityEngine;
using System.Threading.Tasks;
using Unity.Netcode;
using Unity.Services.Authentication;
using Unity.Services.Core;
using Unity.Services.Relay;
using Unity.Services.Relay.Models;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport.Relay;

namespace VR_MNB.InGame.Server
{
    public class InGameServerManager : NetworkBehaviour
    {
        private string joinCode;

        /// <summary>
        /// 호스트로 시작하고 조인 코드를 생성하는 함수
        /// </summary>
        public async Task<string> StartHostWithRelay()
        {
            // 1. Unity Services 초기화 (및 익명 인증)
            await UnityServices.InitializeAsync();

            // TODO: 추후 로그인 되어 있지 않으면 오류메시지 보내는 로직으로 변경 필요
            if (AuthenticationService.Instance.IsSignedIn == false)
            {
                await AuthenticationService.Instance.SignInAnonymouslyAsync();
            }

            // 2. Relay 서버 할당 생성 및 Join Code 얻기
            int maxConnections = 2;
            Allocation allocation = await RelayService.Instance.CreateAllocationAsync(maxConnections);
            joinCode = await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId);
            Debug.Log("Relay Join Code: " + joinCode);

            // 3. NetworkManager 에 Relay 서버 정보 설정
            var relayServerData = new RelayServerData(
                host: allocation.RelayServer.IpV4,
                port: (ushort)allocation.RelayServer.Port,
                allocationId: allocation.AllocationIdBytes,
                connectionData: allocation.ConnectionData,
                hostConnectionData: new byte[0], // 호스트는 hostConnectionData가 필요 없으므로 빈 배열 사용
                key: allocation.Key,
                isSecure: true,
                isWebSocket: false
            );
            UnityTransport transport = NetworkManager.Singleton.GetComponent<UnityTransport>();
            transport.SetRelayServerData(relayServerData);

            // 4. host 시작
            NetworkManager.Singleton.StartHost();

            return joinCode;
        }


        /// <summary>
        /// 클라이언트로 게임에 참여하는 함수
        /// </summary>
        public async void StartClientWithRelay(string joinCode)
        {
            // 1. Unity Services 초기화 (및 익명 인증)
            await UnityServices.InitializeAsync();

            // TODO : 익명이 아닌 유저 정보 받아와야 함
            if (AuthenticationService.Instance.IsSignedIn == false)
            {
                await AuthenticationService.Instance.SignInAnonymouslyAsync();
            }

            // 2. Join Code 로 Relay 서버 할당에 참여
            JoinAllocation joinAllocation = await RelayService.Instance.JoinAllocationAsync(joinCode);

            // 3. NetworkManager 에 Relay 서버 정보 설정
            var relayServerData = new RelayServerData(
                host: joinAllocation.RelayServer.IpV4,
                port: (ushort)joinAllocation.RelayServer.Port,
                allocationId: joinAllocation.AllocationIdBytes,
                connectionData: joinAllocation.ConnectionData,
                hostConnectionData: joinAllocation.HostConnectionData,
                key: joinAllocation.Key,
                isSecure: true,
                isWebSocket: false
            );

            UnityTransport transport = NetworkManager.Singleton.GetComponent<UnityTransport>();
            transport.SetRelayServerData(relayServerData);

            // 4. 클라이언트 시작
            NetworkManager.StartClient();
        }
    }
}

위의 방식을 적용해서 위와 같이 InGameServerManager 를 작성할 수 있습니다.