내일배움캠프/프로젝트

팀프로젝트 진행 - 재시작 문제 해결

서보훈 2024. 12. 3. 20:59

호스트와의 연결이 끊길경우, 호스트가 서버를 종료했을경우 클라이언트 플레이어가 로비로 돌아오도록 수정해주었습니다.

public class NetworkConnectController : MonoBehaviour
{
    public NetworkObject networkGameManager;

    private void Awake()
    {
        if (NetworkManager.Singleton.IsHost)
        {
            NetworkObject netGameManager = Instantiate(networkGameManager);
            netGameManager.Spawn(true);
        }
    }

    //게스트 플레이어의 경우, 서버와 연결이 끊어졌을때 이벤트를 받아서 연결 종료
    private void Start()
    {
        if (NetworkManager.Singleton.IsHost) return;
        NetworkManager.Singleton.OnClientDisconnectCallback += ClientDisconnetCallback;
    }

    //서버를 종료시 해야하는 작업들
    public void DisconnetServer()
    {
        Destroy(NetworkManager.Singleton.gameObject);
        VivoxController.Instance.LeaveVoiceChannel();
        NetworkSceneChanger.Instance.ChangeScene("TestScene_SBH");
    }
    
    //호스트가 서버를 종료하는 메서드
    public void ShutDownButton()
    {
        if (!NetworkManager.Singleton.IsHost) return;
        ClientDisconneted();

        NetworkManager.Singleton.Shutdown(true);
        DisconnetServer();
    }

    //클라이언트 연결 해제
    public void ClientDisconneted()
    {
        for(int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++)
        {
            ulong id = NetworkManager.Singleton.ConnectedClientsIds[i];
            if (id == NetworkManager.Singleton.GetComponent<UnityTransport>().ServerClientId) continue;

            NetworkManager.Singleton.GetComponent<UnityTransport>().DisconnectRemoteClient(id);
        }
    }

    //연결이 끊겼을때 호출되는 메서드
    public void ClientDisconnetCallback(ulong id)
    {
        DisconnetServer();
    }
}

클라이언트 플레이어는 OnClientDisconnectCallback 이벤트가 발생하면 의도적이든 의도적이지 않든 호스트로부터 연결이 끊어졌다고 처리하여 서버에서 나가게 됩니다

※ Vivox 관련 내용은 현재 수정이 더 필요한 상태입니다.

 

호스트 플레이어의 경우, 연결 종료를 시도하는 메서드를 임시로 만들어주었으며, 연결종료를 시도하면 모든 클라이언트와의 연결을 끊은 뒤 서버를 종료하게 됩니다.

 

추가적으로, 게임이 시작되면 무작위로 역할을 부여하도록 만들어주었습니다.

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();

        //SetPlayerRole();

        Debug.Log(isHostProfessor); 
    }

    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("실패");
        }
    }
}

트러블슈팅

  • 로비씬으로 돌아온 후, 다시 로비를 만들고 게임을 시작할경우 게임이 시작되지 않는 문제

로비를 관리하는 LobbyManager는 씬을 넘어가지 않고, Vivox 를 관리하는 VivoxController는 씬을 넘어가는 상태였습니다.

이때, VivoxController에서 LobbyManager 의 이벤트인 OnGameStartEvent 에 구독해둔것이 사라지게되었고, 이벤트에 등록된 메서드가 작동하지 않으면서 게임 시작이 되지 않는 문제가 발생하였습니다.

//LobbyManager
//싱글톤, 해당 오브젝트가 씬을 넘어가지 않도록 재정의
protected async override void Awake()
{
    Create();

    if (Instance != this)
    {
        Destroy(gameObject);
    }

    await UnityServices.InitializeAsync();
}

private void Start()
{
    VivoxController.Instance.OnVivoxLoginEvent += ChangeGameScene;
    VivoxController.Instance.SetupGameStartEvent();

    if(AuthenticationService.Instance.IsSignedIn)
    {
        playerName = AuthenticationService.Instance.Profile;
    }
}

LobbyManager의 Start에서 VivoxController에서 이벤트를 구독하는 메서드를 만들어 호출하는 방식으로 문제를 해결하였습니다.

//VivoxController
private async void Start()
{
    await VivoxService.Instance.InitializeAsync();
}

public async void SetupVivox()
{
    if (!VivoxService.Instance.IsLoggedIn)
    {
        await LoginVivoxAsync();
    }

    OnVivoxLoginEvent?.Invoke();
}

public void SetupGameStartEvent()
{
    LobbyManager.Instance.OnGameStartEvent += SetupVivox;
}

또한, 게임을 재시작할때, 이미 Vivox에 로그인되있는상태이므로, 로그인되있는상태면 로그인 과정을 건너뛰도록 수정해주었습니다.

※ 유니티 서비스에 익명 인증을 하는 시점에 Vivox에 로그인하는것이 좋을수도 있다고 생각하고 있습니다.

 

또한 이러한 오류가 발생하였는데

Cannot start Host while an instance is already running

로비씬으로 돌아오면서 NetworkManager가 복사되었고, 이 NetworkManager에서 호스팅을 끝내지 않은 상태여서 발생하는 문제였습니다.

일단 게임 종료시 네트워크매니저가 붙은 게임오브젝트를 삭제함으로써 해결해주었습니다.

//서버를 종료시 해야하는 작업들
public void DisconnetServer()
{
    Destroy(NetworkManager.Singleton.gameObject);
    VivoxController.Instance.LeaveVoiceChannel();
    NetworkSceneChanger.Instance.ChangeScene("TestScene_SBH");
}

 

이 외에도 NetworkObject 와 Rpc 관련 내용이 있습니다. 이 내용은 따로 정리해두었습니다.

수정하지 못한 내용

재입장시, 이전 게임에서 사용한 vivox 관련 UI들이 남아있는 현상이 발견되었습니다.

아직 해결책을 못찾았으며, 일단 플레이가 멈추지는 않기 때문에 먼저 멀티플레이 진행 내용을 제작한 뒤 수정할 예정입니다.