멀티플레이어 게임은 혼자 즐기는 게임과 차별화되는 강력한 상호작용 경험을 제공합니다. 특히 대전 기반의 멀티플레이어 시스템은 여러 플레이어가 직접 만든 방에 참가하여 실시간으로 대결을 펼치는 매력적인 구성을 제공합니다. 그러나 이러한 시스템을 개발하는 것은 매우 복잡한 작업이기 때문에, 체계적이고 세밀한 코드 설계가 필요합니다. 이 글에서는 Unity와 Mirror 네트워크 라이브러리를 사용하여 방 생성 및 매칭 기능을 포함한 완성도 높은 멀티플레이어 대전 시스템을 구축하는 방법을 단계별로 살펴보겠습니다.
목차
1. 기본 멀티플레이어 시스템 개요
멀티플레이어 시스템은 크게 다음과 같은 기능으로 구성됩니다:
- 로비 및 대기실: 플레이어들이 대결 전에 모일 수 있는 공간.
- 방 생성 및 삭제: 플레이어가 직접 방을 만들고 닫을 수 있는 기능.
- 자동 매칭: 사용자가 특정 방을 찾지 않더라도 자동으로 빈 방에 참여.
- 게임 시작 및 종료: 모든 준비가 완료되면 게임을 시작하고, 게임 종료 시 로비로 복귀.
이제 각 기능을 코드로 구현해 보겠습니다.
2. 전체 시스템 코드 예제
필수 라이브러리와 네임스페이스 설정
using Mirror;
using UnityEngine;
using System.Collections.Generic;
메인 네트워크 매니저 설정
기본적으로 NetworkManager
클래스를 상속받아 방 생성, 참가, 자동 매칭을 관리하는 커스텀 매니저 클래스를 작성합니다.
public class CustomNetworkManager : NetworkManager
{
[Header("Lobby Settings")]
public int maxPlayers = 4;
public List<NetworkConnection> lobbyPlayers = new List<NetworkConnection>();
public override void OnServerAddPlayer(NetworkConnection conn)
{
if (lobbyPlayers.Count < maxPlayers)
{
base.OnServerAddPlayer(conn);
lobbyPlayers.Add(conn);
Debug.Log("플레이어가 방에 참여했습니다.");
}
else
{
Debug.Log("방이 가득 찼습니다. 대기열에 추가합니다.");
}
}
public override void OnServerDisconnect(NetworkConnection conn)
{
lobbyPlayers.Remove(conn);
base.OnServerDisconnect(conn);
Debug.Log("플레이어가 방을 떠났습니다.");
}
public void CreateRoom()
{
if (NetworkServer.active)
{
Debug.LogWarning("이미 서버가 실행 중입니다.");
return;
}
StartHost();
Debug.Log("새로운 방이 생성되었습니다.");
}
public void JoinRoom(string ipAddress)
{
networkAddress = ipAddress;
StartClient();
Debug.Log($"{ipAddress}에 연결 중...");
}
public void LeaveRoom()
{
if (NetworkClient.isConnected)
{
StopClient();
Debug.Log("방을 떠났습니다.");
}
else if (NetworkServer.active)
{
StopHost();
Debug.Log("방을 닫았습니다.");
}
}
}
코드 설명
- OnServerAddPlayer: 서버에 새로 연결된 플레이어가 최대 인원 제한에 따라 방에 추가됩니다. 인원이 가득 찼다면 대기열에 추가합니다.
- OnServerDisconnect: 플레이어가 방을 떠날 때 대기열에서 제거하고, 연결을 종료합니다.
- CreateRoom/JoinRoom: 새 방을 생성하거나, 다른 클라이언트가 특정 방에 연결할 수 있도록 합니다.
- LeaveRoom: 클라이언트 또는 호스트가 방을 떠나거나 방을 닫을 때 사용합니다.
3. 자동 매칭 및 대기열 관리
자동 매칭을 통해 플레이어가 특정 방을 찾지 않고도 빈 방에 자동으로 참가할 수 있도록 기능을 확장합니다.
public void AutoMatch()
{
if (lobbyPlayers.Count < maxPlayers)
{
Debug.Log("빈 방에 자동으로 참여합니다.");
JoinRoom(networkAddress);
}
else
{
Debug.Log("대기열에 추가됩니다.");
}
}
자동 매칭은 빈 방을 탐색하여 직접 참여하거나, 빈 방이 없다면 대기열에 플레이어를 추가합니다.
4. 실제 게임 전환과 상태 관리
로비에서 방으로 이동한 후에는 플레이어가 준비 완료 상태가 되면 게임을 시작할 수 있도록 구현합니다.
public void StartGameIfReady()
{
if (lobbyPlayers.Count == maxPlayers)
{
foreach (var conn in lobbyPlayers)
{
TargetGameStart(conn);
}
}
else
{
Debug.Log("모든 플레이어가 준비되지 않았습니다.");
}
}
// 클라이언트에서 게임 시작을 알리는 함수
[TargetRpc]
void TargetGameStart(NetworkConnection conn)
{
Debug.Log("게임이 시작됩니다!");
// 게임 시작 로직을 여기서 구현
}
설명
- StartGameIfReady: 모든 플레이어가 준비되면 게임을 시작하도록 체크합니다.
- TargetGameStart: 개별 클라이언트에게 게임 시작을 알리는 역할을 합니다.
5. 게임 종료 및 복귀 기능
게임이 종료되면 플레이어를 다시 로비로 복귀시키는 기능을 구현합니다.
public void EndGame()
{
foreach (var conn in lobbyPlayers)
{
TargetReturnToLobby(conn);
}
}
[TargetRpc]
void TargetReturnToLobby(NetworkConnection conn)
{
Debug.Log("게임이 종료되었습니다. 로비로 돌아갑니다.");
// 로비 복귀 로직
}
설명
- EndGame: 게임이 종료되면 모든 플레이어가 로비로 복귀하도록 설정합니다.
- TargetReturnToLobby: 개별 클라이언트가 로비로 돌아가게 하는 RPC 호출입니다.
6. 장점과 단점
장점:
- 모듈화된 구성: 확장 가능한 방 관리 시스템으로 자동 매칭, 대기열, 게임 전환 등 다양한 기능을 모듈화해 구현할 수 있습니다.
- 네트워크 최적화: Mirror 라이브러리로 구축한 안정적이고 효율적인 네트워크 통신을 통해 성능이 뛰어납니다.
단점:
- 복잡한 초기 설정: 서버와 클라이언트, 자동 매칭과 같은 여러 기능을 구성하는 데 다소 복잡한 초기 설정이 필요합니다.
- 트래픽 관리: 대기열 및 매칭에서의 네트워크 트래픽이 많아질 경우 최적화가 필요합니다.
결론
이번 글에서는 Unity를 이용하여 완성도 높은 멀티플레이어 방 생성 및 매칭 시스템을 설계하는 과정을 다뤘습니다. 이 시스템은 모듈화된 구조로 다양한 기능을 쉽게 확장할 수 있으며, 플레이어 간 원활한 상호작용을 제공할 수 있습니다.