using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.SceneManagement; namespace Streamingle.Background.Editor { /// /// 배경 씬 썸네일 캡처 도구 /// public class BackgroundThumbnailCapture : EditorWindow { // 16:9 기본값 private const int DEFAULT_WIDTH = 1920; private const int DEFAULT_HEIGHT = 1080; private Camera _selectedCamera; private int _width = DEFAULT_WIDTH; private int _height = DEFAULT_HEIGHT; private BackgroundSyncSettings _syncSettings; private string _savePath; private string _fileName; private bool _useSceneCamera = true; private Texture2D _previewTexture; private BackgroundSceneDatabase _database; private BackgroundSceneInfo _currentSceneInfo; [MenuItem("Streamingle/Capture Background Thumbnail %#t")] public static void ShowWindow() { var window = GetWindow("썸네일 캡처"); window.minSize = new Vector2(350, 500); window.Show(); window.Initialize(); } private void Initialize() { // 데이터베이스 로드 _database = AssetDatabase.LoadAssetAtPath( "Assets/Resources/Settings/BackgroundSceneDatabase.asset"); // 동기화 설정 로드 _syncSettings = AssetDatabase.LoadAssetAtPath( "Assets/Resources/Settings/BackgroundSyncSettings.asset"); // 설정에서 해상도 가져오기 if (_syncSettings != null) { _width = _syncSettings.thumbnailWidth; _height = _syncSettings.thumbnailHeight; } // 현재 로드된 배경 씬 찾기 FindCurrentBackgroundScene(); // 카메라 찾기 FindSceneCamera(); } private void OnEnable() { Initialize(); } private void OnDestroy() { if (_previewTexture != null) { DestroyImmediate(_previewTexture); } } private void FindCurrentBackgroundScene() { _currentSceneInfo = null; _savePath = ""; _fileName = ""; if (_database == null) return; const string BACKGROUND_PATH = "Assets/ResourcesData/Background"; // 현재 로드된 씬들 중 배경 씬 찾기 for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (string.IsNullOrEmpty(scene.path)) continue; // 데이터베이스에서 찾기 var sceneInfo = _database.FindByPath(scene.path); if (sceneInfo != null) { _currentSceneInfo = sceneInfo; _savePath = Path.GetDirectoryName(sceneInfo.scenePath); _fileName = sceneInfo.sceneName; return; } // 데이터베이스에 없지만 Background 폴더에 있는 씬인 경우 // 데이터베이스를 갱신하고 다시 찾기 if (scene.path.Replace("\\", "/").Contains(BACKGROUND_PATH)) { // 데이터베이스 갱신 RefreshDatabaseFromLoaderWindow(); // 다시 찾기 sceneInfo = _database.FindByPath(scene.path); if (sceneInfo != null) { _currentSceneInfo = sceneInfo; _savePath = Path.GetDirectoryName(sceneInfo.scenePath); _fileName = sceneInfo.sceneName; return; } // 여전히 없으면 수동으로 정보 설정 string scenePath = scene.path.Replace("\\", "/"); _savePath = Path.GetDirectoryName(scenePath); _fileName = scene.name; // 카테고리 추출 (폴더명) string relativePath = scenePath.Substring(BACKGROUND_PATH.Length + 1); string categoryName = relativePath.Split('/')[0]; // 새 씬 정보 생성 및 데이터베이스에 추가 _currentSceneInfo = new BackgroundSceneInfo { sceneName = scene.name, scenePath = scenePath, categoryName = categoryName, thumbnailPath = "" }; _database.scenes.Add(_currentSceneInfo); EditorUtility.SetDirty(_database); AssetDatabase.SaveAssets(); UnityEngine.Debug.Log($"새 배경 씬이 데이터베이스에 추가됨: {scene.name}"); return; } } // 배경 씬을 못 찾으면 활성 씬 사용 var activeScene = SceneManager.GetActiveScene(); if (!string.IsNullOrEmpty(activeScene.path)) { _savePath = Path.GetDirectoryName(activeScene.path); _fileName = activeScene.name; } } private void RefreshDatabaseFromLoaderWindow() { // BackgroundSceneLoaderWindow가 열려있으면 RefreshSceneList 호출 var loaderWindow = GetWindow(false, null, false); if (loaderWindow != null) { // 리플렉션으로 private 메서드 호출 var refreshMethod = typeof(BackgroundSceneLoaderWindow).GetMethod("RefreshSceneList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (refreshMethod != null) { refreshMethod.Invoke(loaderWindow, null); } } else { // 윈도우가 없으면 직접 갱신 RefreshSceneListDirectly(); } // 데이터베이스 다시 로드 _database = AssetDatabase.LoadAssetAtPath( "Assets/Resources/Settings/BackgroundSceneDatabase.asset"); } private void RefreshSceneListDirectly() { if (_database == null) return; const string BACKGROUND_PATH = "Assets/ResourcesData/Background"; _database.scenes.Clear(); if (!Directory.Exists(BACKGROUND_PATH)) { return; } // Background 하위 폴더들 검색 var backgroundFolders = Directory.GetDirectories(BACKGROUND_PATH); foreach (var folderPath in backgroundFolders) { string folderName = Path.GetFileName(folderPath); string sceneFolderPath = Path.Combine(folderPath, "Scene"); if (!Directory.Exists(sceneFolderPath)) { sceneFolderPath = folderPath; } // .unity 파일 검색 (재귀적으로) var sceneFiles = Directory.GetFiles(sceneFolderPath, "*.unity", SearchOption.AllDirectories); foreach (var sceneFile in sceneFiles) { if (sceneFile.Contains("SkyBox")) continue; string assetPath = sceneFile.Replace("\\", "/"); string sceneName = Path.GetFileNameWithoutExtension(sceneFile); string sceneDir = Path.GetDirectoryName(sceneFile); string thumbnailPath = FindThumbnail(sceneDir, sceneName); var sceneInfo = new BackgroundSceneInfo { sceneName = sceneName, scenePath = assetPath, categoryName = folderName, thumbnailPath = thumbnailPath }; _database.scenes.Add(sceneInfo); } } EditorUtility.SetDirty(_database); AssetDatabase.SaveAssets(); } private string FindThumbnail(string directory, string sceneName) { string[] extensions = { ".png", ".jpg", ".jpeg" }; foreach (var ext in extensions) { string thumbnailPath = Path.Combine(directory, sceneName + ext); if (File.Exists(thumbnailPath)) { return thumbnailPath.Replace("\\", "/"); } } return null; } private void FindSceneCamera() { _selectedCamera = null; // 1. 씬에서 Main Camera 찾기 _selectedCamera = Camera.main; // 2. Main Camera가 없으면 아무 카메라나 찾기 if (_selectedCamera == null) { var cameras = FindObjectsByType(FindObjectsSortMode.None); if (cameras.Length > 0) { _selectedCamera = cameras[0]; } } // 3. Scene View 카메라 사용 옵션 if (_selectedCamera == null) { _useSceneCamera = true; } } private void OnGUI() { EditorGUILayout.Space(10); EditorGUILayout.LabelField("배경 썸네일 캡처", EditorStyles.boldLabel); EditorGUILayout.Space(5); // 현재 씬 정보 EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(); if (_currentSceneInfo != null) { EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("현재 배경 씬:", _currentSceneInfo.sceneName); EditorGUILayout.LabelField("카테고리:", _currentSceneInfo.categoryName); EditorGUILayout.EndVertical(); } else { EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField("배경 씬을 찾을 수 없습니다.", EditorStyles.miniLabel); EditorGUILayout.LabelField("배경 씬을 먼저 로드하세요."); EditorGUILayout.EndVertical(); } if (GUILayout.Button("새로고침", GUILayout.Width(70), GUILayout.Height(35))) { Initialize(); UnityEngine.Debug.Log("씬 정보가 새로고침되었습니다."); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); EditorGUILayout.Space(10); // 카메라 선택 EditorGUILayout.LabelField("카메라 설정", EditorStyles.boldLabel); _useSceneCamera = EditorGUILayout.Toggle("Scene View 카메라 사용", _useSceneCamera); if (!_useSceneCamera) { _selectedCamera = (Camera)EditorGUILayout.ObjectField("카메라", _selectedCamera, typeof(Camera), true); // 카메라 목록 버튼 if (GUILayout.Button("씬에서 카메라 찾기")) { ShowCameraMenu(); } } EditorGUILayout.Space(10); // 해상도 설정 EditorGUILayout.LabelField("해상도 설정", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); _width = EditorGUILayout.IntField("너비", _width); _height = EditorGUILayout.IntField("높이", _height); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("1920x1080 (권장)")) { _width = 1920; _height = 1080; } if (GUILayout.Button("1280x720")) { _width = 1280; _height = 720; } if (GUILayout.Button("960x540")) { _width = 960; _height = 540; } EditorGUILayout.EndHorizontal(); EditorGUILayout.HelpBox("16:9 비율 썸네일이 권장됩니다.\n에디터에서는 1:1로 크롭되어 표시되고, 웹사이트에는 16:9 원본이 사용됩니다.", MessageType.Info); EditorGUILayout.Space(10); // 저장 경로 EditorGUILayout.LabelField("저장 설정", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); _savePath = EditorGUILayout.TextField("저장 경로", _savePath); if (GUILayout.Button("...", GUILayout.Width(30))) { string path = EditorUtility.OpenFolderPanel("저장 폴더 선택", _savePath, ""); if (!string.IsNullOrEmpty(path)) { // Assets 폴더 기준 상대 경로로 변환 if (path.StartsWith(Application.dataPath)) { _savePath = "Assets" + path.Substring(Application.dataPath.Length); } else { _savePath = path; } } } EditorGUILayout.EndHorizontal(); _fileName = EditorGUILayout.TextField("파일 이름", _fileName); EditorGUILayout.LabelField($"저장 위치: {_savePath}/{_fileName}.png", EditorStyles.miniLabel); EditorGUILayout.Space(10); // 프리뷰 EditorGUILayout.LabelField("프리뷰", EditorStyles.boldLabel); if (GUILayout.Button("프리뷰 갱신", GUILayout.Height(25))) { CapturePreview(); } if (_previewTexture != null) { float previewHeight = position.width * _previewTexture.height / _previewTexture.width; var previewRect = GUILayoutUtility.GetRect(position.width - 20, Mathf.Min(previewHeight, 300)); GUI.DrawTexture(previewRect, _previewTexture, ScaleMode.ScaleToFit); } EditorGUILayout.Space(10); // 캡처 버튼 GUI.enabled = !string.IsNullOrEmpty(_savePath) && !string.IsNullOrEmpty(_fileName); GUI.backgroundColor = new Color(0.3f, 0.8f, 0.3f); if (GUILayout.Button("썸네일 캡처 및 저장", GUILayout.Height(40))) { CaptureAndSave(); } GUI.backgroundColor = Color.white; GUI.enabled = true; // 모든 배경 씬 썸네일 일괄 캡처 EditorGUILayout.Space(10); EditorGUILayout.LabelField("일괄 캡처", EditorStyles.boldLabel); if (GUILayout.Button("모든 배경 씬 썸네일 캡처", GUILayout.Height(30))) { CaptureAllBackgroundScenes(); } // 썸네일 새로고침 EditorGUILayout.Space(10); if (GUILayout.Button("배경 로더 썸네일 새로고침", GUILayout.Height(25))) { var loaderWindow = GetWindow(false, null, false); if (loaderWindow != null) { loaderWindow.RefreshAllThumbnails(); UnityEngine.Debug.Log("썸네일 캐시가 새로고침되었습니다."); } else { UnityEngine.Debug.LogWarning("배경 씬 로더 윈도우가 열려있지 않습니다."); } } } private void ShowCameraMenu() { var menu = new GenericMenu(); var cameras = FindObjectsByType(FindObjectsSortMode.None); foreach (var cam in cameras) { Camera capturedCam = cam; menu.AddItem(new GUIContent(cam.name), _selectedCamera == cam, () => { _selectedCamera = capturedCam; }); } if (cameras.Length == 0) { menu.AddDisabledItem(new GUIContent("카메라를 찾을 수 없습니다")); } menu.ShowAsContext(); } private void CapturePreview() { if (_previewTexture != null) { DestroyImmediate(_previewTexture); } _previewTexture = CaptureImage(); } private Texture2D CaptureImage() { Camera camera = GetCaptureCamera(); if (camera == null) { UnityEngine.Debug.LogError("캡처할 카메라를 찾을 수 없습니다."); return null; } // RenderTexture 생성 RenderTexture rt = new RenderTexture(_width, _height, 24, RenderTextureFormat.ARGB32); rt.antiAliasing = 8; // 카메라 렌더링 RenderTexture prevRT = camera.targetTexture; camera.targetTexture = rt; camera.Render(); camera.targetTexture = prevRT; // Texture2D로 변환 RenderTexture prevActive = RenderTexture.active; RenderTexture.active = rt; Texture2D texture = new Texture2D(_width, _height, TextureFormat.RGB24, false); texture.ReadPixels(new Rect(0, 0, _width, _height), 0, 0); texture.Apply(); RenderTexture.active = prevActive; // 정리 DestroyImmediate(rt); return texture; } private Camera GetCaptureCamera() { if (_useSceneCamera) { // Scene View 카메라 가져오기 var sceneView = SceneView.lastActiveSceneView; if (sceneView != null) { return sceneView.camera; } } return _selectedCamera; } private void CaptureAndSave() { Texture2D texture = CaptureImage(); if (texture == null) return; // PNG로 인코딩 byte[] bytes = texture.EncodeToPNG(); DestroyImmediate(texture); // 저장 경로 확인 string fullPath = Path.Combine(_savePath, _fileName + ".png"); // 디렉토리 확인 string directory = Path.GetDirectoryName(fullPath); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 파일 저장 File.WriteAllBytes(fullPath, bytes); // 에셋 데이터베이스 갱신 AssetDatabase.Refresh(); // 텍스처 임포트 설정 string assetPath = fullPath.Replace("\\", "/"); TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter; if (importer != null) { importer.textureType = TextureImporterType.Sprite; importer.maxTextureSize = 1024; importer.SaveAndReimport(); } UnityEngine.Debug.Log($"썸네일 저장됨: {fullPath}"); // 프리뷰 업데이트 _previewTexture = AssetDatabase.LoadAssetAtPath(assetPath); // 데이터베이스 갱신 if (_database != null) { if (_currentSceneInfo != null) { _currentSceneInfo.thumbnailPath = assetPath; EditorUtility.SetDirty(_database); AssetDatabase.SaveAssets(); } else { // _currentSceneInfo가 없으면 데이터베이스 전체 갱신 RefreshSceneListDirectly(); } } // 배경 씬 로더 윈도우 갱신 var loaderWindow = GetWindow(false, null, false); if (loaderWindow != null) { // 씬 목록 갱신 (리플렉션) var refreshMethod = typeof(BackgroundSceneLoaderWindow).GetMethod("RefreshSceneList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (refreshMethod != null) { refreshMethod.Invoke(loaderWindow, null); } // 썸네일 캐시 갱신 loaderWindow.RefreshThumbnail(assetPath); } EditorUtility.DisplayDialog("완료", $"썸네일이 저장되었습니다.\n{fullPath}", "확인"); } private void CaptureAllBackgroundScenes() { if (_database == null) { EditorUtility.DisplayDialog("오류", "배경 씬 데이터베이스를 찾을 수 없습니다.", "확인"); return; } if (_database.scenes.Count == 0) { EditorUtility.DisplayDialog("오류", "배경 씬이 없습니다.", "확인"); return; } // 확인 다이얼로그 if (!EditorUtility.DisplayDialog("일괄 캡처", $"총 {_database.scenes.Count}개의 배경 씬 썸네일을 캡처합니다.\n" + "각 씬을 로드하고 카메라를 찾아 캡처합니다.\n\n" + "진행하시겠습니까?", "예", "아니오")) { return; } int successCount = 0; int failCount = 0; try { for (int i = 0; i < _database.scenes.Count; i++) { var sceneInfo = _database.scenes[i]; EditorUtility.DisplayProgressBar("썸네일 캡처 중...", $"{sceneInfo.sceneName} ({i + 1}/{_database.scenes.Count})", (float)i / _database.scenes.Count); // 이미 썸네일이 있으면 스킵 if (!string.IsNullOrEmpty(sceneInfo.thumbnailPath) && File.Exists(sceneInfo.thumbnailPath)) { successCount++; continue; } // 씬 로드 var scene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene( sceneInfo.scenePath, UnityEditor.SceneManagement.OpenSceneMode.Additive); // 카메라 찾기 Camera cam = null; var rootObjects = scene.GetRootGameObjects(); foreach (var obj in rootObjects) { cam = obj.GetComponentInChildren(); if (cam != null) break; } if (cam == null) { cam = Camera.main; } if (cam != null) { // 캡처 _selectedCamera = cam; _useSceneCamera = false; Texture2D texture = CaptureImage(); if (texture != null) { string savePath = Path.GetDirectoryName(sceneInfo.scenePath); string fullPath = Path.Combine(savePath, sceneInfo.sceneName + ".png"); byte[] bytes = texture.EncodeToPNG(); File.WriteAllBytes(fullPath, bytes); DestroyImmediate(texture); sceneInfo.thumbnailPath = fullPath.Replace("\\", "/"); successCount++; } else { failCount++; } } else { UnityEngine.Debug.LogWarning($"카메라를 찾을 수 없음: {sceneInfo.sceneName}"); failCount++; } // 씬 언로드 UnityEditor.SceneManagement.EditorSceneManager.CloseScene(scene, true); } } finally { EditorUtility.ClearProgressBar(); } // 데이터베이스 저장 EditorUtility.SetDirty(_database); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorUtility.DisplayDialog("완료", $"썸네일 캡처 완료\n성공: {successCount}개\n실패: {failCount}개", "확인"); } } }