Enum string 기반 -> AssetReference 기반으로 Scene 관리

This commit is contained in:
NTG_Lenovo 2025-08-01 14:42:31 +09:00
parent 4b24122c3d
commit 8ae8fc0242
3 changed files with 113 additions and 49 deletions

View File

@ -76,15 +76,15 @@ public static async Task<List<T>> LoadAssetsByLabel<T>(string label) where T : U
return new List<T>(); return new List<T>();
} }
public static async Task<SceneInstance> LoadScene(string key, LoadSceneMode mode = LoadSceneMode.Additive) public static async Task<SceneInstance> LoadScene(AssetReference assetReference, LoadSceneMode mode = LoadSceneMode.Additive)
{ {
var handle = Addressables.LoadSceneAsync(key, mode); var handle = Addressables.LoadSceneAsync(assetReference, mode);
await handle.Task; await handle.Task;
if (handle.Status == AsyncOperationStatus.Succeeded) if (handle.Status == AsyncOperationStatus.Succeeded)
return handle.Result; return handle.Result;
Debug.LogError($"Scene load failed: {key}"); Debug.LogError($"Scene load failed: {assetReference}");
return default; return default;
} }

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets;
namespace DDD namespace DDD
{ {
@ -9,5 +10,6 @@ namespace DDD
public class GameFlowSceneMappingSo : SerializedScriptableObject public class GameFlowSceneMappingSo : SerializedScriptableObject
{ {
public Dictionary<GameFlowState, SceneType> FlowToSceneMapping = new(); public Dictionary<GameFlowState, SceneType> FlowToSceneMapping = new();
public Dictionary<SceneType, AssetReference> AssetMapping = new();
} }
} }

View File

@ -2,16 +2,33 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine; using UnityEngine;
using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
namespace DDD namespace DDD
{ {
public enum SceneType public enum SceneType
{ {
Entry = 0, None = 0,
Restaurant = 1, Entry = 1,
Voyage = 2 Restaurant = 2,
Voyage = 3
}
public class SceneData
{
public Scene Scene { get; }
public SceneInstance? SceneInstance { get; } // 처음에 실행되는 씬은 SceneInstance를 가질 수 없음 Unload용 데이터
public SceneData(Scene scene, SceneInstance? sceneInstance)
{
Scene = scene;
SceneInstance = sceneInstance;
}
} }
public class SceneManager : Singleton<SceneManager>, IManager public class SceneManager : Singleton<SceneManager>, IManager
@ -19,37 +36,46 @@ public class SceneManager : Singleton<SceneManager>, IManager
[SerializeField] [SerializeField]
private SceneTransitionHandlerSo _sceneTransitionHandlerSo; private SceneTransitionHandlerSo _sceneTransitionHandlerSo;
private Dictionary<SceneType, SceneInstance> _loadedScenes; private Dictionary<SceneType, SceneData> _loadedSceneDatas;
private SceneType _currentSceneType = SceneType.None;
private SceneInstance _currentSceneInstance;
public Action<SceneInstance> OnSceneChanged;
public void PreInit() public void PreInit()
{ {
Array sceneTypeArray = Enum.GetValues(typeof(SceneType)); Array sceneTypeArray = Enum.GetValues(typeof(SceneType));
_loadedScenes = new Dictionary<SceneType, SceneInstance>(sceneTypeArray.Length); _loadedSceneDatas = new Dictionary<SceneType, SceneData>(sceneTypeArray.Length - 1);
} }
public async Task Init() public async Task Init()
{ {
try var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
var activeScenePath = activeScene.path;
#if UNITY_EDITOR
foreach (var kvp in GameFlowManager.Instance.GameFlowSceneMappingSo.AssetMapping)
{ {
foreach (SceneType sceneType in Enum.GetValues(typeof(SceneType))) var asset = kvp.Value.editorAsset;
if (asset == null) continue;
string assetPath = AssetDatabase.GetAssetPath(asset);
if (string.IsNullOrWhiteSpace(assetPath)) continue;
if (assetPath == activeScenePath)
{ {
if (sceneType == SceneType.Entry) continue; _currentSceneType = kvp.Key;
var currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); _loadedSceneDatas.Add(_currentSceneType, new SceneData(activeScene, null));
if (sceneType.ToString() == currentScene.name) break;
{ }
continue;
} }
await PreloadSceneAsync(sceneType); if (_currentSceneType == SceneType.None)
}
}
catch (Exception e)
{ {
Debug.LogWarning($"Scene preload failed\n{e}"); Debug.LogWarning($"[SceneManager] 활성 씬이 AssetMapping에 존재하지 않습니다: {activeScenePath}");
} }
#else
_currentSceneType = SceneType.Entry;
_loadedSceneDatas.Add(_currentSceneType, new SceneData(activeScene, null));
#endif
await PreloadAll();
} }
public void PostInit() public void PostInit()
@ -57,47 +83,70 @@ public void PostInit()
} }
public SceneInstance GetSceneInstance(SceneType sceneType) => _loadedScenes[sceneType]; public async Task PreloadSceneBySceneType(SceneType sceneType)
public async Task PreloadSceneAsync(SceneType sceneType)
{ {
if (_loadedScenes.ContainsKey(sceneType)) if (_loadedSceneDatas.ContainsKey(sceneType)) return;
return;
string key = sceneType.ToString(); var kvp = GameFlowManager.Instance.GameFlowSceneMappingSo.AssetMapping.FirstOrDefault(kvp => kvp.Key == sceneType);
SceneInstance sceneInstance = await AssetManager.LoadScene(key); var preloadedSceneInstance = await AssetManager.LoadScene(kvp.Value);
if (sceneInstance.Scene.IsValid()) if (preloadedSceneInstance.Scene.IsValid())
{ {
_loadedScenes[sceneType] = sceneInstance; DeactivateScene(preloadedSceneInstance.Scene);
_loadedSceneDatas.Add(kvp.Key, new SceneData(preloadedSceneInstance.Scene, preloadedSceneInstance));
foreach (var root in sceneInstance.Scene.GetRootGameObjects())
{
root.SetActive(false);
}
} }
else else
{ {
Debug.LogError($"[SceneManager] Failed to preload scene: {sceneType}"); Debug.LogError($"[SceneManager] {sceneType}의 씬이 존재하지 않습니다.");
}
}
public async Task PreloadAll()
{
var assetMapping = GameFlowManager.Instance.GameFlowSceneMappingSo.AssetMapping;
foreach (var kvp in assetMapping)
{
if (_loadedSceneDatas.ContainsKey(kvp.Key)) continue;
var preloadedSceneInstance = await AssetManager.LoadScene(kvp.Value);
if (preloadedSceneInstance.Scene.IsValid())
{
DeactivateScene(preloadedSceneInstance.Scene);
_loadedSceneDatas.Add(kvp.Key, new SceneData(preloadedSceneInstance.Scene, preloadedSceneInstance));
}
else
{
Debug.LogError($"[SceneManager] {kvp.Key}의 씬이 존재하지 않습니다.");
}
} }
} }
public async Task ActivateScene(SceneType sceneType) public async Task ActivateScene(SceneType sceneType)
{ {
if (_currentSceneType == sceneType) return;
foreach (var handler in _sceneTransitionHandlerSo.Handlers.Where(handler => handler != null)) foreach (var handler in _sceneTransitionHandlerSo.Handlers.Where(handler => handler != null))
{ {
await handler.OnBeforeSceneActivate(sceneType); await handler.OnBeforeSceneActivate(sceneType);
} }
if (_loadedScenes.TryGetValue(sceneType, out var sceneInstance)) if (_loadedSceneDatas.TryGetValue(sceneType, out var sceneData))
{ {
foreach (var root in sceneInstance.Scene.GetRootGameObjects()) foreach (var root in sceneData.Scene.GetRootGameObjects())
{ {
root.SetActive(true); root.SetActive(true);
} }
UnityEngine.SceneManagement.SceneManager.SetActiveScene(sceneInstance.Scene); if (sceneData.Scene.IsValid())
_currentSceneInstance = sceneInstance; {
UnityEngine.SceneManagement.SceneManager.SetActiveScene(sceneData.Scene);
_currentSceneType = sceneType;
}
else
{
Debug.LogError($"[SceneManager] {sceneType}의 Scene이 유효하지 않습니다.");
}
} }
else else
{ {
@ -112,20 +161,33 @@ public async Task ActivateScene(SceneType sceneType)
public void DeactivateScene(SceneType sceneType) public void DeactivateScene(SceneType sceneType)
{ {
if (_loadedScenes.TryGetValue(sceneType, out var sceneInstance)) if (_loadedSceneDatas.TryGetValue(sceneType, out var sceneData))
{ {
foreach (var root in sceneInstance.Scene.GetRootGameObjects()) foreach (var root in sceneData.Scene.GetRootGameObjects())
{ {
root.SetActive(false); root.SetActive(false);
} }
} }
} }
public async Task UnloadSceneAsync(SceneType sceneType) private void DeactivateScene(Scene scene)
{ {
if (_loadedScenes.TryGetValue(sceneType, out var sceneInstance)) foreach (var root in scene.GetRootGameObjects())
{ {
await AssetManager.UnloadScene(GetSceneInstance(sceneType)); root.SetActive(false);
}
}
public async Task UnloadSceneBySceneType(SceneType sceneType)
{
if (_loadedSceneDatas.TryGetValue(sceneType, out var sceneData))
{
if (sceneData.SceneInstance == null) return;
if (GameFlowManager.Instance.GameFlowSceneMappingSo.AssetMapping.ContainsKey(sceneType))
{
await AssetManager.UnloadScene((SceneInstance)sceneData.SceneInstance);
}
} }
} }
} }