Scene Loading Sample
![Level 4](/v2/img/docs/levels/level03-intermediate_1.5x.png)
개요
Fusion Scene Loading 샘플은 개발자가 필요한 대로 조작할 수 있는 Fusion을 사용한 씬 로딩 방법을 보다 사용자 지정 가능한 방식으로 탐색하고 보여줍니다. 간단한 한 씬 로드 대신, 가산 로드를 사용하여 동시에 로드할 수 있는 최대 4개의 씬을 사용하며, 각 플레이어가 서버에 로드된 씬 중 어떤 장면을 자신 쪽에 로드하고 싶은지 지시할 수 있습니다.
하이라이트
- 씬 로딩 사용자 지정 가능
- 동시에 씬 최대 4개 로드
- 클라이언트 측 씬 관심 관리
다운로드
버전 | 릴리즈 일자 | 다운로드 | ||
---|---|---|---|---|
1.1.3 | Oct 21, 2022 | Fusion Scene Loading 1.1.3 Build 55 |
프로젝트
데모를 실행하기 전에 Photon Cloud 용 Fusion AppId를 생성하고 PhotonAppSettings
에셋에 복사 붙여 넣어야 합니다. 앱 ID는 Photon 관리 화면에서 생성할 수 있습니다. Realtime Id가 아닌 Fusion App Id를 생성해야 합니다.
Fusion 메뉴 Fusion > Realtime Settings
에서 photon 앱 설정 에셋을 선택할 수 있습니다.
생성한 App Id를 App Id Fusion
필드에 붙여 넣으시면 됩니다.
폴더 구조
샘플 코드는 Scripts
폴더에 있습니다. 이 폴더는 다음과 같이 세분됩니다.
- Helpers - 편의를 위한 중요하지 않은 클래스입니다.
- Sample - 샘플이 제공하는 두 씬에서 사용되는 로직입니다.
- CustomSceneObjectProviders - 씬 로딩을 처리하는 샘플 코어 클래스입니다.
샘플 루프
MainScene
에는 세션 이름을 입력하는 필드와 클라이언트 씬 관리를 활성화하기 위한 확인란이 두 개의 버튼과 함께 표시되며, 사용 가능한 두 가지 모드로 들어갑니다.
클라이언트 씬 관리는 세션의 호스트에 대해서만 사용되며 클라이언트는 세션의 호스트에서 속성을 가져옵니다. 제공된 이름의 세션이 없으면 호스트 모드에서 세션이 생성됩니다.
![](/docs/img/fusion/samples/scene-loading/scene-loading-menu.jpg)
사용 가능한 4개의 씬을 참조하는 "Button sample" 4개의 버튼이 표시됩니다. 호스트는 로드하려는 씬을 전환할 수 있으며 클라이언트 씬 관리가 활성화된 경우 로드하려는 씬도 전환해야 합니다.
클라이언트 씬 관리가 활성화된 경우, 클라이언트는 이러한 버튼을 전환하여 로드할 수 있는 씬을 표시할 수 있습니다.
![](/docs/img/fusion/samples/scene-loading/scene-loading-buttons-sample.gif)
"Character sample"에서 각 플레이어는 제어할 캐릭터를 가지고 있으며, 맵의 모서리에 가까이 가면 해당 영역을 참조하는 씬이 로드되고, 영역을 벗어나면 언로드 됩니다. 클라이언트 씬 관리가 활성화된 경우 클라이언트는 현재 씬만 로드합니다.
클라이언트 씬 관리가 활성화된 경우 클라이언트는 허용된 씬만 로드합니다.
![](/docs/img/fusion/samples/scene-loading/scene-loading-character-sample.gif)
클라이언트 씬 관리
클라이언트 씬 관리 로직은 클라이언트가 명시적으로 허용하는 씬만 로드하도록 제한합니다. 이러한 씬은 InterestedInScenes
콜렉션에 의해 지정됩니다. 이렇게 하면 각 클라이언트가 활성 서버 씬의 서로 다른 하위 집합을 로드할 수 있습니다. 호스트/서버는 세션의 상태 권한을 가지고 있므로 모든 씬을 로드해야 합니다.
이 샘플은 복수개의 씬 목록을 처리하고 있습니다:
CurrentScenes
: 호스트/서버에 로드되어 있는 네트워크화된 현재 씬의 목록.InterestedInScenes
: 로컬 클라이언트가 로드할 씬이 들어있습니다. 서버의CurrentScenes
에 씬이 추가되면 해당 씬에 이 컬렉션이 포함된 경우에만 클라이언트에 로드됩니다.LoadedScenes
: 현재 로컬로 로드된 씬입니다.CurrentScenes
또는InterestedInScenes
의 변경 사항이 로드, 로드해제를 트리거 하거나 아무것도 하지 않는 것을 결정하는 데 사용됩니다.
ClientSceneManagement
가 enabled 인 경우 클라이언트는 클라이언트에서 특별히 허용된(InterestedInScenes
) CurrentScenes
항목만 로드합니다.
ClientSceneManagement
가 disabled 이면 클라이언트는 CurrentScenes
컬렉션에 나열된 모든 씬을 로드합니다.
CustomSceneLoaderBase
이 클래스는 NetworkSceneManagerBase
의 몇 가지 변경사항이 있는 복제본입니다. 가장 중요한 변화는 두 가지 추상 메소드를 선언한다는 것입니다.
bool IsScenesUpdated()
: 로드해야 하는 씬이 있으면 true를 리턴합니다.SceneRef GetDesiredSceneToLoad()
: 로드할 장면의SceneRef
를 리턴합니다.
CustomSceneLoader
이 클래스는 CustomSceneLoaderBase
클래스에서 상속되었으며 다음과 같은 추상 메소드를 구현합니다:
SwitchScene()
원래 NetworkSceneManagerBase
클래스에서는 유니티에서 씬 로드를 적절하게 처리하고 로드된 씬에서 발견된 모든 NetworkObjects
를 리턴하기 위한 코루틴이 정의되었습니다. 이 샘플에 사용된 이 변형과의 주요 차이점은 이제 Task
이며 씬을 추가로 로드한다는 것입니다.
C#
protected override async Task<IEnumerable<NetworkObject>> SwitchScene(SceneRef prevScene, SceneRef newScene) {
prevScene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1).buildIndex;
Debug.Log($"Switching Scene from {prevScene} to {newScene}");
List<NetworkObject> sceneObjects = new List<NetworkObject>();
if (newScene >= 0) {
var loadedScene = await LoadSceneAsset(newScene, LoadSceneMode.Additive);
Debug.Log($"Loaded scene {newScene}: {loadedScene}");
sceneObjects = FindNetworkObjects(loadedScene, disable: false);
}
Debug.Log($"Switched Scene from {prevScene} to {newScene} - loaded {sceneObjects.Count} scene objects");
return sceneObjects;
}
IsScenesUpdated()
새 씬을 로드해야 하는 시기를 결정하는 역할을 합니다. 이 구현에서, 적절한 로직를 구현하고 CustomNetworkSceneManager
에서는 로드 및 언로드가 필요한 씬을 확인하는 함수를 호출합니다.
C#
protected override bool IsScenesUpdated() {
if (Runner.SceneManager() && Runner.SceneManager().Object) {
Runner.SceneManager().UnloadOutdatedScenes();
return Runner.SceneManager().IsSceneUpdated(out _desiredScene);
}
return true;
}
GetDesiredSceneToLoad()
로드해야회는 씬을 리턴, 위에서 설명한 것과 같이 IsScenesUpdated()
에 할당된 _desiredScene
입니다.
C#
private SceneRef _desiredScene;
protected override SceneRef GetDesiredSceneToLoad() {
return _desiredScene;
}
CustomNetworkSceneManager
로드할 씬 인덱스들의 목록을 얻으려면 네트워크 컬렉션이 있는 NetworkBehaviour
이 필요합니다. 이 NetworkBehaviour
은 현재 로드된 씬과 로드하려는 씬을 유지하는 역할을 합니다.
C#
public class CustomNetworkSceneManager : NetworkBehaviour
{
private const int MAXSCENES = 4;
[Networked] public NetworkBool ClientSceneManagement { get; set; }
[Networked, Capacity(MAXSCENES)] public NetworkLinkedList<SceneRef> CurrentScenes => default;
public List<SceneRef> InterestedInScenes = new List<SceneRef>(MAXSCENES);
public List<SceneRef> LoadedScenes = new List<SceneRef>(MAXSCENES);
private SceneRef _sceneToUnload;
//...
}
확장 메소드
Spawned()
에서 , CustomNetworkSceneManager
는 NetworkRunner.SetSceneManager()
사용자 지정 확장 메소드의 도움을 통해 NetworkRunner
와 같이 자체적으로 등록합니다.
C#
//CustomNetworkSceneManager.cs
public override void Spawned()
{
if (Runner.SceneManager())
{
Runner.Despawn(Object);
return;
}
DontDestroyOnLoad(this);
Runner.SetSceneManager(this);
}
씬 추가 및 제거
CustomNetworkSceneManager
에는 씬을 추가하거나 제거하는 메소드가 있습니다. 이러한 메소드는 호스트의 [Networked] CurrentScenes
링크드 리스트에 및 클라이언트 측에 있는 InterestedInScenes
목록에 있는 씬 인덱스들을 처리합니다.
씬 업데이트 감지 및 오래된 씬 제거
bool IsSceneUpdated(out SceneRef sceneRef)
는 로드해야 하는 씬이 있으면 true를 반환하고 로드해야 하는 씬의 인덱스에 out
변수를 할당합니다.
C#
public bool IsSceneUpdated( out SceneRef sceneRef )
{
for (int i = 0; i < CurrentScenes.Count; i++) {
if (CurrentScenes[i] == default) continue;
if (LoadedScenes.Contains(CurrentScenes[i])) continue;
if (ClientSceneManagement == false || InterestedInScenes.Contains(CurrentScenes[i])) {
sceneRef = CurrentScenes[i];
LoadedScenes.Add(CurrentScenes[i]);
return false;
}
}
sceneRef = SceneRef.None;
return true;
}
UnloadOutdatedScenes()
함수는 언로드되어야 하는 로드된 씬이 있는지 감지하고 이를 _sceneToUnload
목록에서 제거합니다.
C#
public void UnloadOutdatedScenes(Action<AsyncOperation> removeOutdatedObjects)
{
_sceneToUnload = LoadedScenes.Except(ClientSceneManagement ? InterestedInScenes.Intersect(CurrentScenes) : CurrentScenes).FirstOrDefault();
// Reload scenes is priority
// Reloading scenes is basically done by having a stack of
// scenes to unload and if there's any scene left, pop out and
// set to _sceneToUnload.
if (_scenesToReload.Count > 0) {
_sceneToUnload = _scenesToReload.Pop();
// The host needs to say the clients to reload
// only when finished reloading all scenes.
if (_scenesToReload.Count == 0)
OnUpToDate += TriggerReloadByte;
}
if (_sceneToUnload) {
Debug.Log("Unloading scene - " + _sceneToUnload);
SceneManager.UnloadSceneAsync(_sceneToUnload);
LoadedScenes.Remove(_sceneToUnload);
}
}
Back to top