내일배움캠프/프로젝트

팀 프로젝트 - 오브젝트 스폰 시점 수정, 로딩 씬 추가

서보훈 2024. 12. 9. 21:03

변경점

  • UGS 인증을 별도의 클래스로 만들어주었습니다. 또한 UGS에 로그인을 한 뒤 Vivox에 로그인 하도록 변경하였습니다.
public class NetworkAuthenticate : MonoSingleton<NetworkAuthenticate>
{
    public TMP_InputField nameInput;

    //로그인 성공시 호출
    public event Action OnAuthenticateSuccessEvent;

    private readonly string loginText = "Connecting";

    protected async override void Awake()
    {
        Create();

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

        await UnityServices.InitializeAsync();
    }

    //인증 메서드 - UGS에 연결
    public async void Authenticate()
    {

        LoadingSceneController.Instance.ActiveNetworkLoadingUI(loginText);
        
        string playerName;
        //플레이어 프로필 설정
        playerName = nameInput.text;

        try
        {
            AuthenticationService.Instance.SwitchProfile(playerName);
            //인증 시도
            await AuthenticationService.Instance.SignInAnonymouslyAsync();

            await AuthenticationService.Instance.UpdatePlayerNameAsync(playerName);
        }
        catch (Exception ex)
        {
            Debug.Log(ex);
            Debug.Log("UGS 연결 실패");
        }

        try
        {
            await VivoxAuthenticate();
        }
        catch (Exception ex)
        {
            Debug.Log(ex);
            Debug.Log("Vivox 로그인 실패");
        }

        OnAuthenticateSuccessEvent?.Invoke();
    }

    public async Task VivoxAuthenticate()
    {
        await VivoxService.Instance.InitializeAsync();

        LoginOptions options = new LoginOptions();

        options.DisplayName = AuthenticationService.Instance.PlayerName;

        await VivoxService.Instance.LoginAsync(options);
    }
}

Vivox 를 여기서 로그인하고 게임에 참여할 때는 채팅 채널에 참여하기만 하도록 수정해주었습니다.

 

  • NetworkObject들의 스폰을 클라이언트의 씬 로드후 시점으로 변경하였습니다.
public class NetworkSpawnController : MonoSingleton<NetworkSpawnController>
{
    //...생략

    protected override void Awake()
    {
        Create();

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

        NetworkManager.Singleton.SceneManager.OnSynchronizeComplete += SpawnManager;
    }

    public void SpawnManager(ulong clientId)
    {
        if (NetworkManager.Singleton.IsHost && NetworkManager.ServerClientId != clientId)
        {
            NetworkObject netGameManager = Instantiate(networkGameManager);
            netGameManager.Spawn(true);
        }
    }
    //... 생략
}

현재 NetworkGameManager 오브젝트가 스폰되는것을 기점으로 추가적으로 캐릭터등의 NetworkObject들이 스폰되도록 되어있습니다.

 

NetworkGameManager 의 스폰을 NetworkSpawnController 로 옮겨주었으며, 스폰 메서드를 NetworkSceneManager 의 OnSynchronizeComplete 이벤트에 구독시킴으로써, 클라이언트가 씬 로드를 끝내고 동기화를 끝내면 스폰을 시작하도록 변경해주었습니다.

 

추가된 내용

  • LoadingScene 추가

네트워크 연결, 씬 로드등의 상황에서 버튼등과의 상호작용을 막고, 현재 씬을 로드하거나 인터넷 연결중임을 보여줄 수 있는 UI를 추가해주었습니다

 

이는 로비에서 로그인할때, 로비에서 게임씬으로 넘어갈때, 게임씬에서 로비씬으로 넘어갈때 모두 사용되어야하기 때문에 별도의 씬을 만든 뒤, Addictive 로 씬을 중첩하여 사용하도록 만들어주었습니다.

 

Addictive 형태로 씬 로딩을 변경하면서, 다른 씬으로 넘어가는 스크립트에 변경이 생겼습니다.

public class NetworkSceneChanger : MonoSingleton<NetworkSceneChanger>
{
    private Scene currentScene;
    public string VoiceChannelName { get; set; }

    private void Start()
    {
        currentScene = SceneManager.GetActiveScene();
        SceneManager.LoadScene("LoadingScene", LoadSceneMode.Additive);

        //씬 로드후, ActiveScene 내용을 바꿔주기 위해 사용
        SceneManager.sceneLoaded += OnSceneLoad;
    }

    public void ChangeScene(string sceneName)
    {
        //이전의 Active씬 언로드
        SceneManager.UnloadSceneAsync(currentScene);

        if (NetworkManager.Singleton.IsHost)
        {
            //호스트가 씬 변경후, 클라이언트와 씬을 동기화
            NetworkManager.Singleton.SceneManager.ActiveSceneSynchronizationEnabled = true;
            NetworkManager.Singleton.SceneManager.SetClientSynchronizationMode(LoadSceneMode.Additive);
            NetworkManager.Singleton.SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);
        }
    }

    private void OnSceneLoad(Scene scene, LoadSceneMode mode)
    {
        //로딩씬이 로드될때는 작동하지 않음
        if (scene.name == "LoadingScene") return;

        //ActiveScene 변경
        SceneManager.SetActiveScene(scene);
        currentScene = scene;
    }

    public void ClientChangeScene(string sceneName)
    {
        //연결 해제등의 이유로 서버를 통해서가 아닌 클라이언트가 씬을 변경할때 사용
        SceneManager.UnloadSceneAsync(currentScene);
        SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);

        Scene changedScene = SceneManager.GetSceneByName(sceneName);
        SceneManager.SetActiveScene(changedScene);
        currentScene = changedScene;
    }
}

게임을 시작할때는 NetworkSceneManager를 통해 호스트가 씬 변경을 시도하게됩니다. 이때, 호스트와 클라이언트는 모두 이전의 ActiveScene 인 로비씬을 언로드 합니다.

 

또한, 클라이언트는 호스트와 동기화된 씬을 로드할 수 있도록 설정해주었습니다.

 

씬이 로드되면 클라이언트와 호스트는 개별적으로 SceneManager 의 sceneLoaded 이벤트를 통해 OnSceneLoad 메서드를 작동시켜서 Addictive 로 로드된 씬을 ActiveScene으로 변경하고 현재 씬 정보를 저장합니다.

저장된 씬 정보는 이후 씬을 변경할 때, 해당씬의 언로드를 위해 사용됩니다.

 

Addictive 형태로 씬을 로드할경우, ActiveScene이 이전 씬으로 되어있으며, Instantiate 같은 생성 메서드의 경우 ActiveScene 에서 오브젝트를 생성해주기 때문에 ActiveScene 을 변경해주는 작업이 별도로 필요했습니다.

 

※ 아직 씬이 로드된 뒤 로딩 이미지를 끄는 기능을 만들지 않았습니다.

이 로딩 이미지를 끄는 기능을 만들면서, 로딩이 완료되면 플레이어가 움직일 수 있도록 하는 기능을 추가로 만들게될것 같습니다.

 


트러블슈팅

  • 클라이언트 플레이어의 씬이 로드되지 않는 현상
    • Vivox 로그인 시점을 옮기면서 로딩이 빨라지게되었고, 클라이언트가 연결되고 씬 로드를 하기 전에 호스트의 씬이 로드되고 NetworkObject들을 생성하면서 이러한 문제가 발생한것으로 보입니다.
    • 현재 NetworkManager를 통해서가 아닌, 씬 로드후 플레이어 캐릭터를 스폰하는 방식을 사용하는데 이때 클라이언트에게 소유권이 넘어가지 않는듯한 모습을 보였습니다.
    • 이후, 클라이언트의 씬이 로드된 후 오브젝트들을 생성하면서 문제가 해결되었습니다.
  • 로비씬이 Unload 되지 않는 현상
    • NetworkSceneManager를 통해 Load 와 Unload를 이어서 사용할경우, 아래에 위치한 코드가 작동되지 않는것을 확인하였습니다.
    • 이전씬을 각자 삭제한 뒤, 씬 로드를 NetworkSceneManager를 사용하여 호출하고, 클라이언트의 씬 내용을 호스트에게 동기화 하는것으로 문제를 해결하였습니다.