using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Streamingle.Background { /// /// 런타임 배경 씬 로더 UI (IMGUI) /// Inspector에서 설정하거나 단축키로 토글 가능 /// public class BackgroundSceneLoaderUI : MonoBehaviour { [Header("설정")] [SerializeField] private BackgroundSceneDatabase sceneDatabase; [SerializeField] private KeyCode toggleKey = KeyCode.F2; [SerializeField] private bool showOnStart = false; [Header("UI 설정")] [SerializeField] private int windowWidth = 400; [SerializeField] private int windowHeight = 500; [SerializeField] private int thumbnailSize = 100; private bool _showUI; private Vector2 _scrollPosition; private string _searchFilter = ""; private string _selectedCategory = "전체"; private Dictionary> _categorizedScenes; private Dictionary _categoryFoldouts = new Dictionary(); private Dictionary _loadedThumbnails = new Dictionary(); private Rect _windowRect; private bool _isDragging; private Vector2 _dragOffset; private BackgroundSceneLoader _loader; private string _statusMessage = ""; private float _statusTime; private GUIStyle _headerStyle; private GUIStyle _buttonStyle; private GUIStyle _searchStyle; private GUIStyle _categoryStyle; private bool _stylesInitialized; private void Start() { _showUI = showOnStart; _windowRect = new Rect(20, 20, windowWidth, windowHeight); if (sceneDatabase != null) { _categorizedScenes = sceneDatabase.GetScenesByCategory(); foreach (var category in _categorizedScenes.Keys) { _categoryFoldouts[category] = true; } // 썸네일 로드 LoadThumbnails(); } // 로더 초기화 _loader = BackgroundSceneLoader.Instance; _loader.Database = sceneDatabase; _loader.OnSceneLoadCompleted += OnSceneLoaded; _loader.OnSceneUnloaded += OnSceneUnloaded; _loader.OnError += OnError; } private void OnDestroy() { if (_loader != null) { _loader.OnSceneLoadCompleted -= OnSceneLoaded; _loader.OnSceneUnloaded -= OnSceneUnloaded; _loader.OnError -= OnError; } } private void Update() { if (Input.GetKeyDown(toggleKey)) { _showUI = !_showUI; } } private void LoadThumbnails() { if (sceneDatabase == null) return; foreach (var scene in sceneDatabase.scenes) { if (string.IsNullOrEmpty(scene.thumbnailPath)) continue; if (_loadedThumbnails.ContainsKey(scene.thumbnailPath)) continue; // Resources에서 로드 시도 (런타임에서는 Resources 폴더만 가능) // 에디터에서만 AssetDatabase 사용 가능 #if UNITY_EDITOR var texture = UnityEditor.AssetDatabase.LoadAssetAtPath(scene.thumbnailPath); if (texture != null) { _loadedThumbnails[scene.thumbnailPath] = texture; scene.thumbnail = texture; } #endif } } private void InitStyles() { if (_stylesInitialized) return; _headerStyle = new GUIStyle(GUI.skin.box) { fontSize = 14, fontStyle = FontStyle.Bold, alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } }; _buttonStyle = new GUIStyle(GUI.skin.button) { fontSize = 12 }; _searchStyle = new GUIStyle(GUI.skin.textField) { fontSize = 12 }; _categoryStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = FontStyle.Bold }; _stylesInitialized = true; } private void OnGUI() { if (!_showUI) return; InitStyles(); // 드래그 처리 HandleDrag(); // 윈도우 그리기 _windowRect = GUI.Window(GetInstanceID(), _windowRect, DrawWindow, ""); } private void HandleDrag() { Event e = Event.current; Rect titleBar = new Rect(_windowRect.x, _windowRect.y, _windowRect.width, 30); if (e.type == EventType.MouseDown && titleBar.Contains(e.mousePosition)) { _isDragging = true; _dragOffset = e.mousePosition - new Vector2(_windowRect.x, _windowRect.y); e.Use(); } else if (e.type == EventType.MouseUp) { _isDragging = false; } else if (e.type == EventType.MouseDrag && _isDragging) { _windowRect.position = e.mousePosition - _dragOffset; e.Use(); } } private void DrawWindow(int windowID) { // 타이틀 바 GUI.Box(new Rect(0, 0, _windowRect.width, 30), "배경 씬 로더", _headerStyle); // 닫기 버튼 if (GUI.Button(new Rect(_windowRect.width - 25, 5, 20, 20), "X")) { _showUI = false; } GUILayout.Space(35); // 현재 씬 상태 GUILayout.BeginHorizontal(); string currentSceneName = _loader?.CurrentSceneName ?? "없음"; GUILayout.Label($"현재: {currentSceneName}", GUILayout.Width(200)); bool hasLoadedScene = _loader != null && _loader.CurrentBackgroundScene.HasValue && _loader.CurrentBackgroundScene.Value.IsValid() && _loader.CurrentBackgroundScene.Value.isLoaded; if (hasLoadedScene && GUILayout.Button("언로드", GUILayout.Width(60))) { _loader.UnloadCurrentScene(); } GUILayout.EndHorizontal(); // 검색 GUILayout.BeginHorizontal(); GUILayout.Label("검색:", GUILayout.Width(40)); _searchFilter = GUILayout.TextField(_searchFilter, _searchStyle, GUILayout.Width(150)); if (GUILayout.Button("X", GUILayout.Width(25))) { _searchFilter = ""; } GUILayout.EndHorizontal(); // 카테고리 선택 GUILayout.BeginHorizontal(); GUILayout.Label("카테고리:", GUILayout.Width(60)); var categories = new List { "전체" }; if (_categorizedScenes != null) { categories.AddRange(_categorizedScenes.Keys.OrderBy(x => x)); } int currentIndex = categories.IndexOf(_selectedCategory); if (currentIndex < 0) currentIndex = 0; // 간단한 드롭다운 대체 if (GUILayout.Button(_selectedCategory, GUILayout.Width(100))) { currentIndex = (currentIndex + 1) % categories.Count; _selectedCategory = categories[currentIndex]; } GUILayout.EndHorizontal(); GUILayout.Space(10); // 씬 목록 _scrollPosition = GUILayout.BeginScrollView(_scrollPosition); if (_categorizedScenes != null) { var filteredScenes = GetFilteredScenes(); DrawSceneList(filteredScenes); } GUILayout.EndScrollView(); // 상태 메시지 if (Time.time - _statusTime < 3f && !string.IsNullOrEmpty(_statusMessage)) { GUILayout.Label(_statusMessage); } // 로딩 표시 if (_loader != null && _loader.IsLoading) { GUILayout.Label("로딩 중..."); } } private Dictionary> GetFilteredScenes() { var result = new Dictionary>(); foreach (var kvp in _categorizedScenes) { if (_selectedCategory != "전체" && kvp.Key != _selectedCategory) continue; var scenes = kvp.Value; if (!string.IsNullOrEmpty(_searchFilter)) { scenes = scenes.Where(s => s.sceneName.ToLower().Contains(_searchFilter.ToLower()) || s.categoryName.ToLower().Contains(_searchFilter.ToLower()) ).ToList(); } if (scenes.Count > 0) { result[kvp.Key] = scenes; } } return result; } private void DrawSceneList(Dictionary> scenes) { foreach (var kvp in scenes.OrderBy(x => x.Key)) { string category = kvp.Key; var sceneList = kvp.Value; if (!_categoryFoldouts.ContainsKey(category)) _categoryFoldouts[category] = true; // 카테고리 헤더 GUILayout.BeginHorizontal(); _categoryFoldouts[category] = GUILayout.Toggle(_categoryFoldouts[category], $"{(_categoryFoldouts[category] ? "▼" : "▶")} {category} ({sceneList.Count})", _categoryStyle); GUILayout.EndHorizontal(); if (!_categoryFoldouts[category]) continue; // 씬 목록 foreach (var sceneInfo in sceneList) { DrawSceneItem(sceneInfo); } GUILayout.Space(5); } } private void DrawSceneItem(BackgroundSceneInfo sceneInfo) { string currentName = _loader?.CurrentSceneName ?? ""; bool isCurrentScene = !string.IsNullOrEmpty(currentName) && currentName != "없음" && currentName == sceneInfo.sceneName; GUILayout.BeginHorizontal(GUI.skin.box); // 썸네일 if (sceneInfo.thumbnail != null) { GUILayout.Box(sceneInfo.thumbnail, GUILayout.Width(50), GUILayout.Height(50)); } else { GUILayout.Box("No\nImg", GUILayout.Width(50), GUILayout.Height(50)); } // 씬 정보 GUILayout.BeginVertical(); GUIStyle nameStyle = isCurrentScene ? new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold } : GUI.skin.label; GUILayout.Label(sceneInfo.sceneName, nameStyle); GUILayout.Label(sceneInfo.categoryName, new GUIStyle(GUI.skin.label) { fontSize = 10 }); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); // 로드 버튼 if (GUILayout.Button(isCurrentScene ? "로드됨" : "로드", _buttonStyle, GUILayout.Width(60), GUILayout.Height(40))) { if (!isCurrentScene) { _loader?.LoadScene(sceneInfo); } } GUILayout.EndHorizontal(); } private void OnSceneLoaded(BackgroundSceneInfo sceneInfo) { _statusMessage = $"로드 완료: {sceneInfo.sceneName}"; _statusTime = Time.time; } private void OnSceneUnloaded(string sceneName) { _statusMessage = $"언로드 완료: {sceneName}"; _statusTime = Time.time; } private void OnError(string error) { _statusMessage = $"오류: {error}"; _statusTime = Time.time; UnityEngine.Debug.LogError(error); } /// /// UI 표시 토글 /// public void ToggleUI() { _showUI = !_showUI; } /// /// UI 표시 설정 /// public void SetUIVisible(bool visible) { _showUI = visible; } } }