티스토리 뷰
1. 특정 인원을 만족하면 게임을 시작하기
Unity Mirror 라이브러리를 사용하여 멀티 플레이 환경을 제작하고 있습니다.
NetworkManager를 사용하여 Game Scene에서 4명의 플레이어가 접속하면 게임이 시작되게 할 것입니다.
- GameManager에서 생성된 플레이어의 수를 int 값으로 기록하고 저장합니다.
- 플레이어 객체가 생성되면 Start() 메서드로 GameManager의 int 값을 +1 합니다.
- 그리고 그 수가 4가 된다면 게임을 시작하기 위한 메서드를 실행합니다.
using UnityEngine;
using Mirror;
public class GameManager : NetworkBehaviour
{
// 접속된 플레이어 수를 서버에서 관리하기 위한 네트워크 동기화된 변수
[SyncVar]
private int playerCount = 0;
// 게임이 시작되었는지 여부를 저장하는 플래그
private bool gameStarted = false;
// 플레이어가 추가될 때 호출되는 메서드
[Server]
public void AddPlayer()
{
playerCount++;
// 4명의 플레이어가 모두 접속되면 게임을 시작합니다.
if (playerCount == 4 && !gameStarted)
{
StartGame();
}
}
// 게임을 시작하는 메서드
[Server]
private void StartGame()
{
gameStarted = true;
Debug.Log("Game Started!");
// 게임 시작 로직 추가
RpcStartGameForClients();
}
// 모든 클라이언트에서 게임 시작을 알리는 RPC 메서드
[ClientRpc]
private void RpcStartGameForClients()
{
Debug.Log("All players connected. Game is starting!");
// 클라이언트의 게임 시작 로직 추가 (UI 업데이트, 씬 전환 등)
}
}
using UnityEngine;
using Mirror;
public class Player : NetworkBehaviour
{
// NetworkManager가 스폰할 때 호출되는 Unity 메서드
public override void OnStartServer()
{
base.OnStartServer();
// GameManager에 있는 AddPlayer 메서드를 호출하여 플레이어 수를 증가시킵니다.
GameManager gameManager = FindObjectOfType<GameManager>();
if (gameManager != null)
{
gameManager.AddPlayer();
}
}
}
클라이언트의 연결은 동시에 진행된다고 절대 보장할 수 없습니다.
대신에 마지막 플레이어 오브젝트가 생성되는 순간까지 기다릴 수 있습니다.
다만 여기서 플레이어 오브젝트의 Start()를 사용하는 것 대신
NetworkBehaviour의 OnStartServer()나
OnStartClient(), 또는 OnStartLocalPlayer()를 사용하도록 권합니다.
이 메서드들은 Start() 보다 네트워크 객체의 동기화 작업에 더 적합니다.
2. 플레이어들에게 순서를 정해주기
- GameManager에서 플레이어 객체를 저장할 List를 생성합니다.
- AddPlayer() 메서드는 플레이어를 매개변수로, 그 인자값을 List에 Add() 합니다.
- 그 List의 Count가 4가 되면 List를 셔플하고 그 순서대로 플레이어 객체에 int 값으로 하여 순서를 할당합니다.
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class GameManager : NetworkBehaviour
{
// 플레이어 객체를 저장할 리스트
private List<GameObject> players = new List<GameObject>();
// 게임이 시작되었는지 여부를 저장하는 플래그
private bool gameStarted = false;
// 플레이어 추가 시 호출되는 메서드
[Server]
public void AddPlayer(GameObject player)
{
// 플레이어 리스트에 추가
players.Add(player);
// 플레이어 수가 4명이 되었을 때 게임 시작 로직 실행
if (players.Count == 4 && !gameStarted)
{
StartGame();
}
}
// 게임을 시작하는 메서드
[Server]
private void StartGame()
{
gameStarted = true;
// 플레이어 순서를 셔플
ShufflePlayers();
// 순서대로 플레이어에게 번호 할당
AssignPlayerOrder();
Debug.Log("Game Started!");
RpcStartGameForClients();
}
// 플레이어 리스트를 셔플하는 메서드
private void ShufflePlayers()
{
for (int i = 0; i < players.Count; i++)
{
GameObject temp = players[i];
int randomIndex = Random.Range(i, players.Count);
players[i] = players[randomIndex];
players[randomIndex] = temp;
}
}
// 플레이어들에게 순서를 할당하는 메서드
private void AssignPlayerOrder()
{
for (int i = 0; i < players.Count; i++)
{
// 플레이어 객체에서 Player 스크립트를 가져와 순서 할당
var playerScript = players[i].GetComponent<Player>();
if (playerScript != null)
{
playerScript.SetPlayerOrder(i + 1); // 순서를 1부터 할당
}
}
}
// 클라이언트에서 게임 시작을 알리는 RPC
[ClientRpc]
private void RpcStartGameForClients()
{
Debug.Log("All players connected. Game is starting!");
// 클라이언트의 게임 시작 로직 추가 (UI 업데이트 등)
}
}
using UnityEngine;
using Mirror;
public class Player : NetworkBehaviour
{
[SyncVar]
private int playerOrder; // 플레이어 순서를 저장하는 변수
// NetworkManager가 서버에서 스폰할 때 호출되는 메서드
public override void OnStartServer()
{
base.OnStartServer();
// GameManager에 현재 플레이어 객체를 추가
GameManager gameManager = FindObjectOfType<GameManager>();
if (gameManager != null)
{
gameManager.AddPlayer(gameObject);
}
}
// 서버에서 플레이어 순서를 설정하는 메서드
[Server]
public void SetPlayerOrder(int order)
{
playerOrder = order;
RpcSetPlayerOrder(order); // 클라이언트도 동기화
}
// 클라이언트에서 순서를 동기화하는 메서드
[ClientRpc]
private void RpcSetPlayerOrder(int order)
{
playerOrder = order;
Debug.Log($"Player order set to {playerOrder}");
// 클라이언트에서 플레이어 순서를 UI 등에 반영할 수 있음
}
}
GPT를 사용해 만든 코드일 뿐, 실제 제가 사용한 코드와는 다릅니다.
다만 데이터의 동기화 과정은 크게 다르지 않습니다.
public void AddPlayer(NetworkPlayer player)
{
players.Add(player);
if (isServer)
if (players.Count == NetworkManager.singleton.maxConnections)
{
AssignRandomOrder();
}
}
특별히 서버에서의 작업이 시작되는 AddPlayer()에 대해서는 Server 속성을 추가하기 보단,
if(isServer)로 서버에서만 플레이어 수를 검사 하도록 합니다.
그러면 모든 클라이언트에 있는 GameManager의 List<NetworkPlayer> players에 추가할 수 있습니다.
서버에서만 플레이어 오브젝트 리스트를 관리한다면 각 클라이언트마다
플레이어 오브젝트 간의 간단한 참조가 불가능하거나 불편해질 것입니다.
이후에 플레이어 객체에 순서를 할당 할텐데,
players.Sort((a, b) => a.myOrder.CompareTo(b.myOrder));
이것은 ClientRpc 속성의 메서드에서 Linq의 List.Sort()를 사용하여 다시 정렬할 수 있습니다.
결과적으로 모든 클라이언트의 GameManager의 List에 플레이어 오브젝트가
자신이 할당받은 순서대로 List에 저장될 것입니다.
만약 클라이언트에 플레이어 오브젝트가 추가될 때마다 순서를 지명해주려 한다면,
인원이 충족되기 전에 연결이 끊기는 플레이어가 생길때마다
조금 번거로운 일이 벌어질 수 있습니다...
3. 정리
- OnStartSevet / Client / LocalPlayer() 콜백 메서드를 사용할 수 있다.
- SyncVar 속성의 변수는 서버에서 변경 되어야만 모든 클라이언트에서 동기화 된다. Server나 Command 속성의 메서드를 사용할 수 있다. 또는 if(isServer)를 사용하여 서버에서만 변경되게 할 수 있다.
- Command나 ClientRpc 등의 네트워크 속성 메서드의 매개변수는 inf, float, bool, string 값을 사용할 수 있다. (배열이나 리스트 사용 가능)
'게임 개발 > 게임 개발 프로젝트' 카테고리의 다른 글
카드 게임 개발 일지 #03 (0) | 2024.09.10 |
---|---|
카드 게임 개발 일지 #02 (6) | 2024.09.07 |
카드 게임 개발 일지 #00 (0) | 2024.09.07 |
[팀 프로젝트] 'The Last Hunt' 개발 후기 - 03 (0) | 2024.08.23 |
[팀 프로젝트] 'The Last Hunt' 개발 후기 - 02 (0) | 2024.08.20 |