Unity Multiplayer Services — Lobby, Relay, Netcode 연동
Unity Gaming Services(UGS)는 별도 서버 없이 멀티플레이어를 구현할 수 있는 클라우드 서비스 묶음입니다. Lobby(방 목록/매칭), Relay(NAT 우회 P2P), Netcode for GameObjects(동기화)를 조합해 완전한 멀티플레이어 흐름을 만들 수 있습니다.
1. 패키지 설치
섹션 제목: “1. 패키지 설치”Package Manager:- com.unity.services.lobby- com.unity.services.relay- com.unity.netcode.gameobjects- com.unity.services.authentication2. UGS 초기화 및 인증
섹션 제목: “2. UGS 초기화 및 인증”using Unity.Services.Core;using Unity.Services.Authentication;using UnityEngine;
public class UGSInitializer : MonoBehaviour{ async void Start() { await UnityServices.InitializeAsync();
if (!AuthenticationService.Instance.IsSignedIn) await AuthenticationService.Instance.SignInAnonymouslyAsync();
Debug.Log($"플레이어 ID: {AuthenticationService.Instance.PlayerId}"); }}3. Lobby — 방 생성 및 참가
섹션 제목: “3. Lobby — 방 생성 및 참가”using Unity.Services.Lobbies;using Unity.Services.Lobbies.Models;
public class LobbyManager : MonoBehaviour{ private Lobby _currentLobby;
// 방 생성 public async Task<Lobby> CreateLobbyAsync(string lobbyName, int maxPlayers) { var options = new CreateLobbyOptions { IsPrivate = false, Data = new Dictionary<string, DataObject> { ["map"] = new DataObject( DataObject.VisibilityOptions.Public, "Forest") } };
_currentLobby = await LobbyService.Instance .CreateLobbyAsync(lobbyName, maxPlayers, options);
// 호스트는 하트비트로 로비 유지 StartCoroutine(HeartbeatLobby(_currentLobby.Id)); return _currentLobby; }
// 방 목록 조회 및 참가 public async Task JoinPublicLobbyAsync() { var lobbies = await LobbyService.Instance.QueryLobbiesAsync();
if (lobbies.Results.Count == 0) return;
_currentLobby = await LobbyService.Instance .JoinLobbyByIdAsync(lobbies.Results[0].Id); }
IEnumerator HeartbeatLobby(string lobbyId) { while (true) { yield return new WaitForSeconds(15f); LobbyService.Instance.SendHeartbeatPingAsync(lobbyId); } }}4. Relay — NAT 우회 연결
섹션 제목: “4. Relay — NAT 우회 연결”using Unity.Services.Relay;using Unity.Services.Relay.Models;using Unity.Netcode;using Unity.Netcode.Transports.UTP;
public class RelayManager : MonoBehaviour{ // 호스트: Relay 할당 생성 public async Task<string> StartHostWithRelayAsync(int maxConnections = 4) { var allocation = await RelayService.Instance .CreateAllocationAsync(maxConnections);
string joinCode = await RelayService.Instance .GetJoinCodeAsync(allocation.AllocationId);
var transport = NetworkManager.Singleton.GetComponent<UnityTransport>(); transport.SetHostRelayData( allocation.RelayServer.IpV4, (ushort)allocation.RelayServer.Port, allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData);
NetworkManager.Singleton.StartHost(); return joinCode; }
// 클라이언트: Join Code로 연결 public async Task JoinWithRelayAsync(string joinCode) { var allocation = await RelayService.Instance .JoinAllocationAsync(joinCode);
var transport = NetworkManager.Singleton.GetComponent<UnityTransport>(); transport.SetClientRelayData( allocation.RelayServer.IpV4, (ushort)allocation.RelayServer.Port, allocation.AllocationIdBytes, allocation.Key, allocation.ConnectionData, allocation.HostConnectionData);
NetworkManager.Singleton.StartClient(); }}5. Lobby + Relay 통합 흐름
섹션 제목: “5. Lobby + Relay 통합 흐름”public class MultiplayerManager : MonoBehaviour{ public async Task HostGameAsync() { // 1. Relay 할당 → Join Code 획득 string joinCode = await relayManager.StartHostWithRelayAsync();
// 2. Lobby에 Join Code 저장 var updateOptions = new UpdateLobbyOptions { Data = new Dictionary<string, DataObject> { ["joinCode"] = new DataObject( DataObject.VisibilityOptions.Member, joinCode) } }; await LobbyService.Instance.UpdateLobbyAsync( _currentLobby.Id, updateOptions); }
public async Task JoinGameAsync(string lobbyId) { // 1. Lobby 참가 var lobby = await LobbyService.Instance.JoinLobbyByIdAsync(lobbyId);
// 2. Lobby에서 Join Code 추출 string joinCode = lobby.Data["joinCode"].Value;
// 3. Relay로 연결 await relayManager.JoinWithRelayAsync(joinCode); }}6. NetworkVariable로 상태 동기화
섹션 제목: “6. NetworkVariable로 상태 동기화”using Unity.Netcode;
public class PlayerHealth : NetworkBehaviour{ private NetworkVariable<int> _health = new(100, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
public override void OnNetworkSpawn() { _health.OnValueChanged += (oldVal, newVal) => UpdateHealthUI(newVal); }
[ServerRpc] public void TakeDamageServerRpc(int damage) { _health.Value = Mathf.Max(0, _health.Value - damage); }}Lobby로 방을 관리하고 Relay로 NAT 우회 P2P 연결을 수립한 뒤 Netcode for GameObjects로 상태를 동기화하는 것이 UGS 멀티플레이어의 표준 흐름입니다. Join Code를 Lobby 데이터에 저장해 두 서비스를 연결하는 패턴이 핵심입니다.