This document is about: FUSION 1
SWITCH TO

수정중인 페이지 입니다.

Scene Loading

개요

퓨전은 씬의 고유한 개념을 가지고 있지 않습니다. 대신 퓨전은 INetworkSceneObjectProvider 인터페이스를 제공하며, 개발자가 씬 변경에 대한 사용자 지정 처리를 정의할 수 있는 씬 객체 공급자라고 합니다.

씬 객체 공급자 인스턴스는 StartGameArgsSceneObjectProvider를 통해 NetworkRunner.StartGame() 메소드에서 할당할 수 있습니다. 구현이 제공되지 않은 경우(null) Fusion은 기본 인스턴스인 NetworkSceneManagerDefault를 생성하고 사용합니다.

씬 객체 공급자를 정의하는 옵션은 다음과 같습니다:

  • INetworkSceneObjectProvider
    사용자 지정 씬 객체 공급자를 정의하기 위해서 이 인터페이스를 구현합니다.
  • NetworkSceneManagerBase
    일부 표준 기본 처리가 이미 구현된 INetworkSceneObjectProvider의 추상 구현입니다. 이 클래스에서 파생된 클래스를 확장하여 사용자 지정 씬 객체 공급자를 정의합니다.
  • NetworkSceneManagerDefault
    NetworkSceneManagerBase에서 파생된 INetworkSceneObjectProvider의 완벽한 프로토타입 구현입니다. 이 클래스는 그대로 사용하거나 필요에 따라 확장 및 재정의할 수 있습니다. 또한 사용자 자신의 구현을 구현하는 방법에 대한 예시가 될 수도 있습니다. 이 클래스는 NetworkRunnerSceneObjectProvider가 제공되지 않을 때 사용되는 기본 씬 객체 공급자입니다.

INetworkSceneObjectProvider

클래스를 NetworkRunner에 대해 씬 객체 공급자로 할당하려면 해당 클래스에서 다음 메소드를 사용하여 INetworkSceneObjectProvider 인터페이스를 구현해야 합니다:

void Initialize(NetworkRunner runner)
NetworkRunner 인스턴스가 필요할 수 있는 모든 초기화를 구현합니다. Fusion이 NetworkRunner.Initialize() 처리의 일부로 호출합니다.

void Shutdown(NetworkRunner runner)
NetworkRunner 종료 프로세스의 일부로 Fusion에 의해 구현이 호출됩니다.

bool TryResolveSceneObject(NetworkRunner runner, Guid sceneObjectGuid, out NetworkObject instance)
지정된 sceneObjectGuid와 일치하는 씬 NetworkObject에 대한 찾기 작업을 구현합니다.

bool IsReady(NetworkRunner runner)
현재 러너 씬 로드가 완료되었는지 여부를 확인하는 작업입니다.

NetworkSceneManagerBase

NetworkSceneManagerBaseINetworkSceneObjectProvider 인터페이스를 구현하는 추상 클래스로, 사용자 정의 씬 객체 공급자를 만드는 시작점으로 사용할 수 있습니다.

새 씬을 로드해야 하는 시점을 감지하고 비동기 로드 프로세스를 관리합니다. 또한 NetworkRunner.RegisterUniqueObjects()를 사용하여 씬의 풀링 된 모든 NetworkObject에 대해 시뮬레이션 객체를 할당합니다.

NetworkSceneManagerBase에서 상속되는 사용자 정의 씬 객체 공급자를 구현할 때 추상 IEnumerator SwitchScene() 메소드의 구현 필요합니다. 이 메소드는 씬을 로드하고 로드된 후 해당 씬에서 발견된 모든 NetworkObjects를 수집한 다음 해당 컬렉션을 finished delegate에게 전달해야 합니다. 그 외에도 개발자는 적합하다고 생각되는 모든 기능을 자유롭게 추가할 수 있습니다.

또한 개발자는 INetworkSceneObjectProvider의 완전한 사용자 정의 클래스 구현을 만들 수 있습니다.

NetworkSceneManagerDefault

NetworkRunner에 사용자 지정 구현이 제공되지 않는 경우 NetworkSceneManagerDefault는 기본 씬 객체 공급자입니다. NetworkSceneManagerBase 에서 상속하고 추상 코루틴을 구현하여 씬을 로컬로 통일하고 해당 씬에서 발견된 모든 NetworkObject를 풀링 합니다.

사용자 지정 관리자는 개발자가 생성할 수 있으며 NetworkSceneManagerBase에서 상속하고 SceneObjectProvider로 할당할 수 있습니다.

NetworkSceneManagerDefault씬 개체 공급자(제공되지 않은 경우 NetworkRunner 기본값)로 사용될 수 있으며 사용 가능한 재정의를 사용하여 확장될 수도 있습니다.

씬 업데이트 감지

로드할 새 씬이 있는지 감지해야 합니다. NetworkSceneManagerBaseLateUpdate() 함수를 사용하여 현재 로드된 씬이 NetworkRunner.CurrentScene에서 제공하는 씬과 다른지 확인합니다.

C#

private SceneRef _currentScene;
private bool _currentSceneOutdated;

protected virtual void LateUpdate() 
{
    if (!Runner) 
        return;

    if (Runner.CurrentScene != _currentScene) 
        _currentSceneOutdated = true;
}

중요 노트: NetworkRunner.CurrentScene은 씬 변경을 감지하는 데 클라이언트가 사용하는 하나의 SceneRef 값만 네트워크로 연결합니다. 이것은 간단한 구현이며 개발자는 자신의 컬렉션을 보관하여 여러 씬을 참조하고 NetworkRunner.CurrentScene를 사용하지 않도록 관리할 수 있습니다.

씬 로딩 이미 진행 중

현재 씬이 오래된 경우 외에 수행해야 하는 또 다른 중요한 검사로는 씬이 현재 비동기적으로 로드 중인지 확인하고 완료될 때까지 새 로드를 연기하여 개체를 오버라이드 하거나 손실되지 않도록 하는 것입니다.

C#

if (!_currentSceneOutdated || _runningCoroutine != null) 
{
    // busy or up to date
    return;
}

INetworkSceneObjectProvider.IsReady의 구현

NetworkSceneManagerBaseINetworkSceneObjectProvider을 구현하므로 NetworkRunner가 씬 객체를 연결할 준비가 되었을 때 이를 알려 주는 IsReady() 구현을 제공해야 합니다.

C#

bool INetworkSceneObjectProvider.IsReady(NetworkRunner runner) 
{
    Assert.Check(Runner == runner);

    if (_runningCoroutine != null) 
        return false;
    
    if (_currentSceneOutdated) 
        return false;
    
    if (runner.CurrentScene != _currentScene) 
        return false;

    return true;
}

로드 씬 코루틴이 실행 중이거나 현재 씬이 오래된 경우 false, 그렇지 않으면 true가 반환됩니다.

SwitchSceneWrapper

SwitchSceneWrapper는 씬 로드 프로세스를 처리하는 적절한 코루틴입니다.

C#

LateUpdate()
{
    // All the previous checks

    var prevScene = _currentScene;
      _currentScene = Runner.CurrentScene;
      _currentSceneOutdated = false;

      _runningCoroutine = SwitchSceneWrapper(prevScene, _currentScene);
      StartCoroutine(_runningCoroutine);
}

코루틴을 올바르게 실행하는 방법은 다음과 같습니다:

로드된 새 씬 개체를 보유할 일부 제어 변수와 딕셔너리가 생성되고 NetworkSceneManagerDefault에 의해 구현된 추상 SwitchScene() 메소드가 호출되고 일부 확인 후 결과 목록 객체가 NetworkRunner에 저장 및 등록됩니다.

C#

private IEnumerator SwitchSceneWrapper(SceneRef prevScene, SceneRef newScene)
{
    bool finishCalled = false;
    Dictionary<Guid, NetworkObject> sceneObjects = new Dictionary<Guid, NetworkObject>();

    //...
    //Call and execution of abstratc SwitchScene()
    //...

    if (error != null) 
        LogError($"Failed to switch scenes: {error}");
    else if (!finishCalled) 
        LogError($"Failed to switch scenes: SwitchScene implementation did not invoke finished delegate");
    else 
    {
        _sceneObjects = sceneObjects;
        Runner.RegisterUniqueObjects(_sceneObjects.Values);
    }
}

SwitchScene 기본 구현

NetworkSceneManagerDefault에 의해 구현된 SwitchScene() 메소드는 다음 작업을 수행합니다.

  1. 현재 씬을 언로드합니다(현재 씬이 유효한 경우).
  2. 유니티 LoadSceneAsync() 메소드를 호출합니다.
  3. 대기 시간이 완료될 때까지 추가 실행을 수행합니다(씬 로드 완료).
  4. 대기 시간이 완료되고 로드된 씬이 유효하지 않으면 오류가 발생하고 종료됩니다.
  5. 로드된 씬에서 각 NetworkObject를 찾아 저장합니다.
    • 발견된 모든 NetworkObject 게임 객체를 비활성화합니다(나중에 연결되면 활성화됨).
    • (멀티 피어 모드의 경우) 각 NetworkObject를 Runner Visibility 처리로 등록합니다.
  6. 발견된 NetworkObject 컬렉션을 인수로 전달하는 Finished() delegate를 트리거 합니다.
PeerMode.Multiple에서 씬을 로드하려면 특정 러너의 물리 씬에 객체를 삽입하고 러너의 가시성 처리에 `NetworkObject`를 추가하는 특별한 처리가 필요합니다.

C#

    //The following code is a simplified overview.

    //The coroutine itself. 
    //It selects the proper handling method based on the peer mode.
    protected override IEnumerator SwitchScene(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) {
      if (Runner.Config.PeerMode == NetworkProjectConfig.PeerModes.Single) {
        return SwitchSceneSinglePeer(prevScene, newScene, finished);
      } else {
        return SwitchSceneMultiplePeer(prevScene, newScene, finished);
      }
    }

    //Summary of the single peer implementation.
    protected virtual IEnumerator SwitchSceneSinglePeer(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) {

      // Creating and defining needed variables.
      Scene loadedScene;
      Scene activeScene = SceneManager.GetActiveScene();

      LoadSceneParameters loadSceneParameters = new LoadSceneParameters(LoadSceneMode.Single);

      loadedScene = default;

      yield return LoadSceneAsync(newScene, loadSceneParameters, scene => loadedScene = scene);

      if (!loadedScene.IsValid()) {
        throw new InvalidOperationException($"Failed to load scene {newScene}: async op failed");
      }

      //Delay frames for safety
      for (int i = PostLoadDelayFrames; i > 0; --i) {
        yield return null;
      }

      var sceneObjects = FindNetworkObjects(loadedScene, disable: true);
      finished(sceneObjects);
    }
Back to top