Scene Loading
개요
퓨전은 씬의 고유한 개념을 가지고 있지 않습니다. 대신 퓨전은 INetworkSceneObjectProvider
인터페이스를 제공하며, 개발자가 씬 변경에 대한 사용자 지정 처리를 정의할 수 있는 씬 객체 공급자라고 합니다.
씬 객체 공급자 인스턴스는 StartGameArgs
의 SceneObjectProvider
를 통해 NetworkRunner.StartGame()
메소드에서 할당할 수 있습니다. 구현이 제공되지 않은 경우(null) Fusion은 기본 인스턴스인 NetworkSceneManagerDefault
를 생성하고 사용합니다.
씬 객체 공급자를 정의하는 옵션은 다음과 같습니다:
INetworkSceneObjectProvider
사용자 지정 씬 객체 공급자를 정의하기 위해서 이 인터페이스를 구현합니다.NetworkSceneManagerBase
일부 표준 기본 처리가 이미 구현된INetworkSceneObjectProvider
의 추상 구현입니다. 이 클래스에서 파생된 클래스를 확장하여 사용자 지정 씬 객체 공급자를 정의합니다.NetworkSceneManagerDefault
NetworkSceneManagerBase
에서 파생된INetworkSceneObjectProvider
의 완벽한 프로토타입 구현입니다. 이 클래스는 그대로 사용하거나 필요에 따라 확장 및 재정의할 수 있습니다. 또한 사용자 자신의 구현을 구현하는 방법에 대한 예시가 될 수도 있습니다. 이 클래스는NetworkRunner
에SceneObjectProvider
가 제공되지 않을 때 사용되는 기본 씬 객체 공급자입니다.
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
NetworkSceneManagerBase
는 INetworkSceneObjectProvider
인터페이스를 구현하는 추상 클래스로, 사용자 정의 씬 객체 공급자를 만드는 시작점으로 사용할 수 있습니다.
새 씬을 로드해야 하는 시점을 감지하고 비동기 로드 프로세스를 관리합니다. 또한 NetworkRunner.RegisterUniqueObjects()
를 사용하여 씬의 풀링 된 모든 NetworkObject
에 대해 시뮬레이션 객체를 할당합니다.
NetworkSceneManagerBase
에서 상속되는 사용자 정의 씬 객체 공급자를 구현할 때 추상 IEnumerator SwitchScene()
메소드의 구현 필요합니다. 이 메소드는 씬을 로드하고 로드된 후 해당 씬에서 발견된 모든 NetworkObjects
를 수집한 다음 해당 컬렉션을 finished
delegate에게 전달해야 합니다. 그 외에도 개발자는 적합하다고 생각되는 모든 기능을 자유롭게 추가할 수 있습니다.
또한 개발자는 INetworkSceneObjectProvider
의 완전한 사용자 정의 클래스 구현을 만들 수 있습니다.
NetworkSceneManagerDefault
NetworkRunner
에 사용자 지정 구현이 제공되지 않는 경우 NetworkSceneManagerDefault
는 기본 씬 객체 공급자입니다. NetworkSceneManagerBase
에서 상속하고 추상 코루틴을 구현하여 씬을 로컬로 통일하고 해당 씬에서 발견된 모든 NetworkObject
를 풀링 합니다.
사용자 지정 관리자는 개발자가 생성할 수 있으며 NetworkSceneManagerBase
에서 상속하고 SceneObjectProvider로 할당할 수 있습니다.
NetworkSceneManagerDefault
는 씬 개체 공급자(제공되지 않은 경우 NetworkRunner
기본값)로 사용될 수 있으며 사용 가능한 재정의를 사용하여 확장될 수도 있습니다.
씬 업데이트 감지
로드할 새 씬이 있는지 감지해야 합니다. NetworkSceneManagerBase
는 LateUpdate()
함수를 사용하여 현재 로드된 씬이 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의 구현
NetworkSceneManagerBase
가 INetworkSceneObjectProvider
을 구현하므로 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()
메소드는 다음 작업을 수행합니다.
- 현재 씬을 언로드합니다(현재 씬이 유효한 경우).
- 유니티
LoadSceneAsync()
메소드를 호출합니다. - 대기 시간이 완료될 때까지 추가 실행을 수행합니다(씬 로드 완료).
- 대기 시간이 완료되고 로드된 씬이 유효하지 않으면 오류가 발생하고 종료됩니다.
- 로드된 씬에서 각
NetworkObject
를 찾아 저장합니다.- 발견된 모든
NetworkObject
게임 객체를 비활성화합니다(나중에 연결되면 활성화됨). - (멀티 피어 모드의 경우) 각 NetworkObject를 Runner Visibility 처리로 등록합니다.
- 발견된 모든
- 발견된
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