using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; namespace DDD { 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, IManager { [SerializeField] private SceneTransitionHandlerSo _sceneTransitionHandlerSo; private readonly Dictionary _loadedSceneDatas = new(); private readonly Dictionary _assetKeyToSceneData = new(); public void PreInit() { _loadedSceneDatas.Clear(); _assetKeyToSceneData.Clear(); } public async Task Init() { var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); #if UNITY_EDITOR var flowToSceneMapping = GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping; foreach (var pair in flowToSceneMapping) { var editorAsset = pair.Value.editorAsset; if (editorAsset == null) continue; var path = AssetDatabase.GetAssetPath(editorAsset); if (string.IsNullOrWhiteSpace(path) == false && path == activeScene.path) { var data = new SceneData(activeScene, null); _loadedSceneDatas[pair.Key] = data; _assetKeyToSceneData[GetRuntimeKey(pair.Value)] = data; break; } } #else var data = new SceneData(activeScene, null); _loadedSceneDatas[GameFlowState.None] = data; #endif await PreloadAll(); } public void PostInit() { } public async Task PreloadScene(GameFlowState gameFlowState) { if (_loadedSceneDatas.ContainsKey(gameFlowState)) return; if (!GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping.TryGetValue(gameFlowState, out var assetRef)) { Debug.LogError($"[SceneManager] {gameFlowState}의 AssetReference가 없습니다."); return; } var runtimeKey = GetRuntimeKey(assetRef); if (_assetKeyToSceneData.TryGetValue(runtimeKey, out var existing)) { _loadedSceneDatas[gameFlowState] = existing; return; } var loadedInstance = await AssetManager.LoadScene(assetRef); if (!loadedInstance.Scene.IsValid()) { Debug.LogError($"[SceneManager] {gameFlowState}의 씬 로딩 실패"); return; } DeactivateScene(loadedInstance.Scene); var data = new SceneData(loadedInstance.Scene, loadedInstance); _loadedSceneDatas[gameFlowState] = data; _assetKeyToSceneData[runtimeKey] = data; } public async Task PreloadAll() { var flowToSceneMapping = GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping; foreach (var pair in flowToSceneMapping) { if (_loadedSceneDatas.ContainsKey(pair.Key)) continue; var runtimeKey = GetRuntimeKey(pair.Value); if (_assetKeyToSceneData.TryGetValue(runtimeKey, out var existing)) { _loadedSceneDatas[pair.Key] = existing; continue; } var instance = await AssetManager.LoadScene(pair.Value); if (!instance.Scene.IsValid()) { Debug.LogError($"[SceneManager] {pair.Key}의 씬 로딩 실패"); continue; } DeactivateScene(instance.Scene); var data = new SceneData(instance.Scene, instance); _loadedSceneDatas[pair.Key] = data; _assetKeyToSceneData[runtimeKey] = data; } } public async Task ActivateScene(GameFlowState newFlowState) { if (!_loadedSceneDatas.TryGetValue(newFlowState, out var sceneData)) { Debug.LogError($"[SceneManager] Scene not loaded: {newFlowState}"); return; } var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene(); if (sceneData.Scene == activeScene) { Debug.Log($"[SceneManager] 이미 활성화된 씬입니다: {newFlowState}"); return; } foreach (var handler in _sceneTransitionHandlerSo.Handlers.Where(handler => handler != null)) { await handler.OnBeforeSceneActivate(); } foreach (var root in sceneData.Scene.GetRootGameObjects()) { root.SetActive(true); } if (sceneData.Scene.IsValid()) { UnityEngine.SceneManagement.SceneManager.SetActiveScene(sceneData.Scene); } else { Debug.LogError($"[SceneManager] {newFlowState}의 Scene이 유효하지 않습니다."); } foreach (var handler in _sceneTransitionHandlerSo.Handlers.Where(handler => handler != null)) { await handler.OnAfterSceneActivate(); } } public void DeactivateScene(GameFlowState gameFlowState) { if (_loadedSceneDatas.TryGetValue(gameFlowState, out var sceneData)) { DeactivateScene(sceneData.Scene); } } private void DeactivateScene(Scene scene) { foreach (var root in scene.GetRootGameObjects()) { root.SetActive(false); } } public async Task UnloadScene(GameFlowState gameFlowState) { if (_loadedSceneDatas.TryGetValue(gameFlowState, out var sceneData)) { if (sceneData.SceneInstance == null) return; if (GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping .TryGetValue(gameFlowState, out var assetRef)) { _assetKeyToSceneData.Remove(GetRuntimeKey(assetRef)); } _loadedSceneDatas.Remove(gameFlowState); await AssetManager.UnloadScene(sceneData.SceneInstance.Value); } } private string GetRuntimeKey(AssetReference reference) => reference.RuntimeKey.ToString(); } }