Scene Loading Sample
개요
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
에는 세션 이름을 입력하는 필드와 클라이언트 씬 관리를 활성화하기 위한 확인란이 두 개의 버튼과 함께 표시되며, 사용 가능한 두 가지 모드로 들어갑니다.
클라이언트 씬 관리는 세션의 호스트에 대해서만 사용되며 클라이언트는 세션의 호스트에서 속성을 가져옵니다. 제공된 이름의 세션이 없으면 호스트 모드에서 세션이 생성됩니다.
사용 가능한 4개의 씬을 참조하는 "Button sample" 4개의 버튼이 표시됩니다. 호스트는 로드하려는 씬을 전환할 수 있으며 클라이언트 씬 관리가 활성화된 경우 로드하려는 씬도 전환해야 합니다.
클라이언트 씬 관리가 활성화된 경우, 클라이언트는 이러한 버튼을 전환하여 로드할 수 있는 씬을 표시할 수 있습니다.
"Character sample"에서 각 플레이어는 제어할 캐릭터를 가지고 있으며, 맵의 모서리에 가까이 가면 해당 영역을 참조하는 씬이 로드되고, 영역을 벗어나면 언로드 됩니다. 클라이언트 씬 관리가 활성화된 경우 클라이언트는 현재 씬만 로드합니다.
클라이언트 씬 관리가 활성화된 경우 클라이언트는 허용된 씬만 로드합니다.
클라이언트 씬 관리
클라이언트 씬 관리 로직은 클라이언트가 명시적으로 허용하는 씬만 로드하도록 제한합니다. 이러한 씬은 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