팀원이 제작한 플레이어 오브젝트를 네트워크 오브젝트화 하여 멀티플레이를 구현하였습니다.
캐릭터 생성 로직
네트워크 오브젝트를 스폰하기 위한 클래스를 생성해주었습니다.
이후 모든 오브젝트의 생성을 이 클래스가 당담할 예정입니다.
public class NetworkSpawnController : MonoSingleton<NetworkSpawnController>
{
public Transform profassorSpawnPoint;
public Transform studentSpawnPoint;
public NetworkObject playerPrefab;
protected override void Awake()
{
Create();
if (Instance != this)
{
Destroy(gameObject);
}
}
public void SpawnPlayer(ulong professorId, ulong studentId)
{
if (!NetworkManager.Singleton.IsHost) return;
if(professorId == NetworkManager.Singleton.GetComponent<UnityTransport>().ServerClientId)
{
NetworkObject professor = Instantiate(playerPrefab, profassorSpawnPoint.position, Quaternion.identity);
professor.SpawnAsPlayerObject(professorId);
NetworkObject student = Instantiate(playerPrefab, studentSpawnPoint.position, Quaternion.identity);
student.SpawnAsPlayerObject(studentId);
}
else
{
NetworkObject student = Instantiate(playerPrefab, studentSpawnPoint.position, Quaternion.identity);
student.SpawnAsPlayerObject(studentId);
NetworkObject professor = Instantiate(playerPrefab, profassorSpawnPoint.position, Quaternion.identity);
professor.SpawnAsPlayerObject(professorId);
}
}
}
두 역할의 캐릭터를 당담할 클라이언트 ID를 가져온 뒤, 캐릭터를 생성하고 캐릭터의 소유권을 부여해줍니다.
아래는 역할을 분배하고 위 메서드를 호출하는 네트워크용 게임 매니저 클래스입니다.
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; }
private void Start()
{
networkManager = NetworkManager.Singleton;
transport = networkManager.GetComponent<UnityTransport>();
RoleDicider();
NetworkSpawnController.Instance.SpawnPlayer(ProfessorId, StudentId);
}
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);
}
[ClientRpc]
private void SetPlayerClientRpc(int index)
{
isHostProfessor = index;
SetPlayerRole();
}
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("실패");
}
}
}
이 클래스는 스폰되면 호스트에서 역할 분배를 한 뒤, 클라이언트들에게 역할 내용을 전달하는 역할을 합니다.
역할 분배와 전달이 끝나면 캐릭터 스폰 메서드를 호출합니다.
이후 네트워크 게임 진행에 필요한 메서드는 이 클래스에서 만들게될 예정입니다.
트러블 슈팅
- 클라이언트 캐릭터가 잘못된 위치에 생성되는 문제 - 아직 해결하지 못했습니다. 일단 호스트 캐릭터를 먼저 생성했을때 문제가 해결되었었는데, 이후 다시 문제가 발생한 상태입니다.
- 다수의 AudioListener 가 존재한다는 오류 발생
→ 멀티플레이 진행시, 플레이어가 각자 카메라를 가지게될경우 다른 플레이어의 카메라, AudioListener 를 비활성화 해줄 필요가 있다고 합니다.
따라서 생성시 소유자가 자신이 아니면 카메라가 부착된 자식 오브젝트를 비활성화 하도록 만들었습니다
private void Start()
{
if (!IsOwner)
{
GetComponentInChildren<Camera>().gameObject.SetActive(false);
return;
}
if (cameraTransform == null)
{
cameraTransform = Camera.main.transform;
}
initialPosition = cameraTransform.localPosition;
}
이 내용 또한 아래의 내용에 맞추어 작동 위치를 바꾸어주어야 할 필요가 있다고 생각하고있습니다.
이번 작업에서는 NetworkObject를 Spawn() 메서드를 통해 생성할경우, 유니티 생명주기가 Awake 종류 후 시점이라는 점을 모르는 상태로 진행했다가 시간이 많이끌린감이 있습니다.
또한, 현재는 스폰시 OnNetworkSpawn() 메서드가 호출된다는것을 알게되어 Awake 관련 내용을 여기서 처리해줄 수 있다는걸 알게되어 NetworkGameManager 클래스 내용을 이에 맞추어 수정해줄 예정입니다.
//private void Awake()
//{
// characterController = GetComponent<CharacterController>();
// playerInput = new PlayerInput();
// moveAction = playerInput.Player.Move;
// runAction = playerInput.Player.Run;
// lookAction = playerInput.Player.Look;
// lightAction = playerInput.Player.Light;
// inverntoryAction = playerInput.Player.Inventory;
// Cursor.lockState = CursorLockMode.Locked;
//}
public override void OnNetworkSpawn()
{
playerInput = new PlayerInput();
if (!IsOwner)
{
playerInput.Disable();
return;
}
characterController = GetComponent<CharacterController>();
playerInput.Enable();
lightAction.performed += OnLightAction;
}
'내일배움캠프 > 프로젝트' 카테고리의 다른 글
팀 프로젝트 - 오브젝트 스폰 시점 수정, 로딩 씬 추가 (0) | 2024.12.09 |
---|---|
팀 프로젝트 - 리팩토링 대상 정리 (0) | 2024.12.05 |
팀프로젝트 진행 - 재시작 문제 해결 (0) | 2024.12.03 |
팀 프로젝트 - Vivox 조작 UI 제작 (0) | 2024.12.02 |
팀프로젝트 - 프로토타입 로비 완성, Vivox 연동 시작 (0) | 2024.11.29 |