테스트용 구조물 배치 후, 맵의 정보를 저장하는 스크립트를 만들어주었습니다.
일단 임시로 맵 프리팹에 스크립트를 붙여 맵 정보를 저장해둔 상태입니다.
이후 맵 에디터를 본격적으로 만들때 Json 형태로 저장하는 방식을 사용할 가능성이 있습니다.
호스트측에서 맵을 결정한 후, 클라이언트에 맵을 생성해줄 방법이 필요했습니다.
따라서 MapListSO 라는 이름으로 맵들을 저장하는 스크립터블 오브젝트를 만든 뒤, 리소스파일에 배치해주었습니다.
맵이 정해진 후, ClientRpc 를 사용하여 int값으로 인덱스를 보내주고, 클라이언트는 해당 리스트에 접근한 뒤, 받은 인덱스 값에 위치한 맵을 생성하게됩니다.
맵 데이터와 리스트 SO, 유령의 순찰 경로를 지정해주는 클래스 입니다.
[System.Serializable]
public class MapData : MonoBehaviour
{
//플레이어들 시작 위치
public Vector3 professorSpawnPos;
public Vector3 studentSpawnPos;
//아이템 생성 위치
public List<Vector3> itemSpawnPos;
//퍼즐 정보...TODO
//유령 정보
public Ghost ghostPrefab;
public List<Vector3> ghostSpawnPos;
public List<GhostPatrolPositions> patrolPos;
}
[CreateAssetMenu(fileName = "MapDataList", menuName = "MapSO/Map List")]
public class MapListSO : ScriptableObject
{
public List<MapData> maps;
}
[System.Serializable]
public class GhostPatrolPositions
{
public List<Vector3> patrolPositions = new List<Vector3>();
}
맵의 생성은 NetworkGameManager 에서 생성하도록 만들어주었습니다
해당 클래스는 NetworkSpawnController 로 인해 클라이언트의 씬 로딩이 끝나면 스폰되는 클래스로, 양쪽이 동기화되어야 하는 오브젝트의 생성, Rpc 를 사용해야하는 부분을 관리합니다.
public class NetworkGameManager : NetworkBehaviour
{
private NetworkManager networkManager;
private UnityTransport transport;
private int isHostProfessor = -1;
public ulong ProfessorId { get; private set; }
public ulong StudentId { get; private set; }
public bool HostReadyFlag { get; private set; }
public bool ClientReadyFlag { get; private set; }
//생성 완료 플래그
private bool isRoleDicided = false;
private bool isMapCreated = false;
public override void OnNetworkSpawn()
{
GameManager.Instance.networkGameManager = this;
networkManager = NetworkManager.Singleton;
transport = networkManager.GetComponent<UnityTransport>();
DoNextStep();
}
private void RoleDicider()
{
if (!IsHost) return;
ulong clientId = 0;
for(int i = 0; i < networkManager.ConnectedClientsList.Count; i++)
{
if (networkManager.ConnectedClientsIds[i] == transport.ServerClientId) continue;
clientId = networkManager.ConnectedClientsIds[i];
}
int random = Random.Range(0, 2);
if(random == 0)
{
isHostProfessor = 0;
ProfessorId = transport.ServerClientId;
StudentId = clientId;
}
else
{
isHostProfessor = 1;
ProfessorId = clientId;
StudentId = transport.ServerClientId;
}
SetPlayerClientRpc(isHostProfessor);
isRoleDicided = true;
DoNextStep();
}
[ClientRpc]
private void SetPlayerClientRpc(int index)
{
isHostProfessor = index;
SetPlayerRole();
}
[ClientRpc]
public void SpawnCompliteClientRpc()
{
LoadingSceneController.Instance.CloseGameLoadingPanel();
}
private void SetPlayerRole()
{
if (networkManager.IsHost) return;
if(isHostProfessor == 0)
{
ProfessorId = transport.ServerClientId;
StudentId = networkManager.LocalClientId;
}
else if (isHostProfessor == 1)
{
ProfessorId = networkManager.LocalClientId;
StudentId = transport.ServerClientId;
}
else
{
Debug.Log("실패");
}
isRoleDicided = true;
}
private void MapSelector()
{
if(!IsHost) return;
//TODO : 차후에 맵을 무작위로 선택하게 만들기
MapListSO mapList = ResourceManager.Instance.LoadAsset<MapListSO>("MapDataList", eAssetType.Data, eCategoryType.SO);
GameManager.Instance.mapData = mapList.maps[0];
Debug.Log(GameManager.Instance.mapData);
CreateMapClientRpc(0);
CreateMap();
HostReadyFlag = true;
ReadyComplete();
}
[ClientRpc]
private void CreateMapClientRpc(int index)
{
if (IsHost) return;
MapListSO mapList = ResourceManager.Instance.LoadAsset<MapListSO>("MapDataList", eAssetType.Data, eCategoryType.SO);
GameManager.Instance.mapData = mapList.maps[index];
CreateMap();
ClientReadyServerRpc();
}
private void CreateMap()
{
Instantiate(GameManager.Instance.mapData, Vector3.zero, Quaternion.identity);
isMapCreated = true;
}
[ServerRpc(RequireOwnership = false)]
private void ClientReadyServerRpc()
{
if(!IsHost) return;
ClientReadyFlag = true;
ReadyComplete();
}
private void ReadyComplete()
{
if(ClientReadyFlag && HostReadyFlag)
{
ClientReadyFlag = false;
HostReadyFlag = false;
DoNextStep();
}
}
private void DoNextStep()
{
if (!isRoleDicided)
{
RoleDicider();
}
else if (!isMapCreated)
{
MapSelector();
}
else
{
if (IsHost)
{
NetworkSpawnController.Instance.SpawnPlayer(ProfessorId, StudentId);
}
}
}
}
현재 스폰 진행방식을 이러합니다.
- NetworkGameManager 스폰
- 호스트의 플레이어 역할 분배
- 클라이언트에게 플레이어 역할 정보 전달, 호스트의 맵 선택 시작
- 클라이언트에게 선택된 맵 정보 전달, 양측 모두 맵 생성 시작
- 맵 생성 완료 대기
- 생성 완료가 확인되면 호스트를 통해 플레이어 캐릭터 생성
- 양쪽의 플레이어 생성이 완료되면 로딩이미지를 끄고 게임 시작
또한 플레이어 생성시, 위치를 맵 데이터에 존재하는 생성 위치로 변경해주었습니다.
public void SpawnPlayer(ulong professorId, ulong studentId)
{
if (!NetworkManager.Singleton.IsHost) return;
Debug.Log(playerPrefab.name);
Debug.Log(GameManager.Instance.mapData.name);
NetworkObject professor = Instantiate(playerPrefab, GameManager.Instance.mapData.professorSpawnPos, Quaternion.identity);
professor.SpawnAsPlayerObject(professorId, true);
NetworkObject student = Instantiate(playerPrefab, GameManager.Instance.mapData.studentSpawnPos, Quaternion.identity);
student.SpawnAsPlayerObject(studentId, true);
GameManager.Instance.networkGameManager.SpawnCompliteClientRpc();
}
해야할 일
- 유령의 순찰 루트 지정방식 수정
- 순찰 루트를 정하는 메서드에 매개변수를 줄 예정, 매개변수로 순찰 루트 클래스가 가진 리스트를 넘겨서 순찰 경로를 지정
- 퍼즐의 배치방법
- 현재 정해지지 않음... 어떻게 퍼즐에 필요한 내용을 생성하고 배치해야하는지 고민중
- 아이템 생성
- 맵 생성하기 전, 생성할 아이템과 위치를 정해서 넘겨주어야함.
- 다수의 아이템을 다수의 위치중 하나에 어떻게 생성시킬것인지 고민할 필요가 있음
- 유령 순찰루트 지정방식 수정
- 유령 생성 기능 추가
'내일배움캠프 > 프로젝트' 카테고리의 다른 글
팀 프로젝트 - 퍼즐 관련 멀티플레이 연결 (1) | 2024.12.19 |
---|---|
팀 프로젝트 - 유령 배치, Navmesh 동적 생성 (0) | 2024.12.17 |
팀 프로젝트 - 아이템 기반 (1) | 2024.12.13 |
팀 프로젝트 - 맵 에디터 기반 제작 (0) | 2024.12.11 |
팀 프로젝트 - 클라이언트 캐릭터 위치문제 (0) | 2024.12.10 |