user 17a9f57d59 Feat: Notion 배경 동기화 시스템 및 배경 씬 로더 개선
- Notion 동기화 기능 추가:
  - NotionSyncSettings.cs: Notion API 설정 ScriptableObject
  - NotionBackgroundSync.cs: Notion API 연동 및 동기화 윈도우
  - 배경 씬 정보를 Notion 데이터베이스에 자동 동기화
  - Git Raw URL을 통한 썸네일 이미지 연동
  - Git 커밋 상태 확인 및 경고 표시

- 배경 씬 로더 버그 수정:
  - 리컴파일 후 배경 씬 중복 로드 문제 해결
  - OnFocus 콜백으로 상태 동기화 강화
  - 중복 씬 자동 감지 및 언로드

- 썸네일 캡처 개선:
  - 기본 해상도 1920x1080 (16:9)
  - 에디터에서 1:1 중앙 크롭 표시
  - 캡처 후 자동 Notion 동기화 옵션

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 01:25:19 +09:00

1627 lines
60 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
namespace Streamingle.Background.Editor
{
/// <summary>
/// 원래 라이팅 세팅을 저장하는 클래스
/// </summary>
[Serializable]
public class OriginalLightingSettings
{
// RenderSettings
public Material skybox;
public Light sun;
public AmbientMode ambientMode;
public Color ambientSkyColor;
public Color ambientEquatorColor;
public Color ambientGroundColor;
public Color ambientLight;
public float ambientIntensity;
public Color subtractiveShadowColor;
// Fog
public bool fog;
public Color fogColor;
public FogMode fogMode;
public float fogDensity;
public float fogStartDistance;
public float fogEndDistance;
// 원래 씬의 라이트/볼륨 활성 상태
public List<LightBackup> originalLights = new List<LightBackup>();
public List<VolumeBackup> originalVolumes = new List<VolumeBackup>();
public List<DirectionalLightBackup> originalDirectionalLights = new List<DirectionalLightBackup>();
public List<ComponentBackup> originalNiloToonOverriders = new List<ComponentBackup>();
[Serializable]
public class LightBackup
{
public Light light;
public bool wasEnabled;
}
[Serializable]
public class VolumeBackup
{
public Volume volume;
public bool wasEnabled;
}
[Serializable]
public class DirectionalLightBackup
{
public Light light;
public bool wasEnabled;
}
[Serializable]
public class ComponentBackup
{
public MonoBehaviour component;
public bool wasEnabled;
}
public void Capture(Scene scene)
{
// RenderSettings 저장
skybox = RenderSettings.skybox;
sun = RenderSettings.sun;
ambientMode = RenderSettings.ambientMode;
ambientSkyColor = RenderSettings.ambientSkyColor;
ambientEquatorColor = RenderSettings.ambientEquatorColor;
ambientGroundColor = RenderSettings.ambientGroundColor;
ambientLight = RenderSettings.ambientLight;
ambientIntensity = RenderSettings.ambientIntensity;
subtractiveShadowColor = RenderSettings.subtractiveShadowColor;
// Fog
fog = RenderSettings.fog;
fogColor = RenderSettings.fogColor;
fogMode = RenderSettings.fogMode;
fogDensity = RenderSettings.fogDensity;
fogStartDistance = RenderSettings.fogStartDistance;
fogEndDistance = RenderSettings.fogEndDistance;
// 씬의 라이트/볼륨 상태 저장
originalLights.Clear();
originalVolumes.Clear();
originalDirectionalLights.Clear();
originalNiloToonOverriders.Clear();
var roots = scene.GetRootGameObjects();
foreach (var root in roots)
{
// [Background Lighting]은 제외
if (root.name == "[Background Lighting]") continue;
var lights = root.GetComponentsInChildren<Light>(true);
foreach (var light in lights)
{
originalLights.Add(new LightBackup
{
light = light,
wasEnabled = light.enabled
});
// Directional Light 별도 저장
if (light.type == LightType.Directional)
{
originalDirectionalLights.Add(new DirectionalLightBackup
{
light = light,
wasEnabled = light.enabled
});
}
}
var volumes = root.GetComponentsInChildren<Volume>(true);
foreach (var volume in volumes)
{
originalVolumes.Add(new VolumeBackup
{
volume = volume,
wasEnabled = volume.enabled
});
}
// NiloToonCharacterMainLightOverrider 찾기
var niloToonOverriders = root.GetComponentsInChildren<MonoBehaviour>(true)
.Where(mb => mb != null && mb.GetType().Name == "NiloToonCharacterMainLightOverrider");
foreach (var overrider in niloToonOverriders)
{
originalNiloToonOverriders.Add(new ComponentBackup
{
component = overrider,
wasEnabled = overrider.enabled
});
}
}
}
/// <summary>
/// 배경 씬 로드 시 기존 씬의 Directional Light와 NiloToonOverrider 비활성화
/// </summary>
public void DisableOriginalLighting()
{
// Directional Light 비활성화
foreach (var backup in originalDirectionalLights)
{
if (backup.light != null)
{
backup.light.enabled = false;
}
}
// NiloToonCharacterMainLightOverrider 비활성화
foreach (var backup in originalNiloToonOverriders)
{
if (backup.component != null)
{
backup.component.enabled = false;
}
}
int lightCount = originalDirectionalLights.Count(b => b.light != null);
int overriderCount = originalNiloToonOverriders.Count(b => b.component != null);
if (lightCount > 0 || overriderCount > 0)
{
UnityEngine.Debug.Log($"기존 씬 라이팅 비활성화: Directional Light {lightCount}개, NiloToonOverrider {overriderCount}개");
}
}
/// <summary>
/// 배경 씬 언로드 시 기존 씬의 Directional Light와 NiloToonOverrider 복원
/// </summary>
public void RestoreOriginalLighting()
{
// Directional Light 복원
foreach (var backup in originalDirectionalLights)
{
if (backup.light != null)
{
backup.light.enabled = backup.wasEnabled;
}
}
// NiloToonCharacterMainLightOverrider 복원
foreach (var backup in originalNiloToonOverriders)
{
if (backup.component != null)
{
backup.component.enabled = backup.wasEnabled;
}
}
int lightCount = originalDirectionalLights.Count(b => b.light != null);
int overriderCount = originalNiloToonOverriders.Count(b => b.component != null);
if (lightCount > 0 || overriderCount > 0)
{
UnityEngine.Debug.Log($"기존 씬 라이팅 복원: Directional Light {lightCount}개, NiloToonOverrider {overriderCount}개");
}
}
public void Restore()
{
// RenderSettings 복원
RenderSettings.skybox = skybox;
RenderSettings.sun = sun;
RenderSettings.ambientMode = ambientMode;
RenderSettings.ambientSkyColor = ambientSkyColor;
RenderSettings.ambientEquatorColor = ambientEquatorColor;
RenderSettings.ambientGroundColor = ambientGroundColor;
RenderSettings.ambientLight = ambientLight;
RenderSettings.ambientIntensity = ambientIntensity;
RenderSettings.subtractiveShadowColor = subtractiveShadowColor;
// Fog 복원
RenderSettings.fog = fog;
RenderSettings.fogColor = fogColor;
RenderSettings.fogMode = fogMode;
RenderSettings.fogDensity = fogDensity;
RenderSettings.fogStartDistance = fogStartDistance;
RenderSettings.fogEndDistance = fogEndDistance;
// 라이트/볼륨 상태 복원
foreach (var backup in originalLights)
{
if (backup.light != null)
{
backup.light.enabled = backup.wasEnabled;
}
}
foreach (var backup in originalVolumes)
{
if (backup.volume != null)
{
backup.volume.enabled = backup.wasEnabled;
}
}
// Directional Light와 NiloToonOverrider 복원
RestoreOriginalLighting();
}
}
/// <summary>
/// 배경 씬 로더 에디터 윈도우
/// </summary>
public class BackgroundSceneLoaderWindow : EditorWindow
{
private const string BACKGROUND_PATH = "Assets/ResourcesData/Background";
private const string DATABASE_PATH = "Assets/Resources/Settings/BackgroundSceneDatabase.asset";
private const int THUMBNAIL_SIZE = 128;
private const int GRID_PADDING = 10;
private BackgroundSceneDatabase _database;
private Dictionary<string, List<BackgroundSceneInfo>> _categorizedScenes;
private Dictionary<string, bool> _categoryFoldouts = new Dictionary<string, bool>();
private Dictionary<string, Texture2D> _thumbnailCache = new Dictionary<string, Texture2D>();
private Vector2 _scrollPosition;
private string _searchFilter = "";
private string _selectedCategory = "전체";
private int _viewMode = 0; // 0: 그리드, 1: 리스트
private bool _isLoading;
private float _loadProgress;
private Scene? _loadedBackgroundScene;
private string _currentSceneName = "없음";
// 라이팅 옵션
private bool _copyLighting = true;
private bool _copySkybox = true;
private bool _copyAmbient = true;
private bool _copyFog = true;
private bool _showLightingOptions = false;
// 원래 라이팅 세팅 백업
private OriginalLightingSettings _originalLightingSettings = new OriginalLightingSettings();
private bool _hasBackup = false;
private bool _autoRestoreOnUnload = true;
private static readonly string[] VIEW_MODE_OPTIONS = { "그리드", "리스트" };
private static readonly Color SELECTED_COLOR = new Color(0.3f, 0.6f, 1f, 0.3f);
private static readonly Color HOVER_COLOR = new Color(1f, 1f, 1f, 0.1f);
[MenuItem("Streamingle/Background Scene Loader %#b")]
public static void ShowWindow()
{
var window = GetWindow<BackgroundSceneLoaderWindow>("배경 씬 로더");
window.minSize = new Vector2(400, 500);
window.Show();
}
private void OnEnable()
{
LoadOrCreateDatabase();
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnSceneOpened;
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed += OnSceneClosed;
// 리컴파일 후에도 현재 상태 복원
UpdateCurrentSceneStatus();
}
private void OnFocus()
{
// 윈도우가 포커스될 때마다 상태 동기화
UpdateCurrentSceneStatus();
Repaint();
}
private void OnDisable()
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= OnSceneOpened;
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed -= OnSceneClosed;
ClearThumbnailCache();
}
private void OnSceneOpened(Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
{
// 씬이 열릴 때 현재 씬 상태 업데이트
UpdateCurrentSceneStatus();
// Background 폴더의 씬이면 데이터베이스에 있는지 확인하고 없으면 갱신
if (!string.IsNullOrEmpty(scene.path) && scene.path.Replace("\\", "/").Contains(BACKGROUND_PATH))
{
var sceneInfo = _database?.FindByPath(scene.path);
if (sceneInfo == null)
{
// 데이터베이스에 없으면 씬 목록 갱신
RefreshSceneList();
}
}
Repaint();
}
private void OnSceneClosed(Scene scene)
{
UpdateCurrentSceneStatus();
Repaint();
}
private void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredEditMode ||
state == PlayModeStateChange.EnteredPlayMode)
{
// 씬 상태 업데이트
UpdateCurrentSceneStatus();
Repaint();
}
}
private void LoadOrCreateDatabase()
{
_database = AssetDatabase.LoadAssetAtPath<BackgroundSceneDatabase>(DATABASE_PATH);
if (_database == null)
{
// Resources/Settings 폴더 확인/생성
string dirPath = Path.GetDirectoryName(DATABASE_PATH);
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
_database = CreateInstance<BackgroundSceneDatabase>();
AssetDatabase.CreateAsset(_database, DATABASE_PATH);
AssetDatabase.SaveAssets();
}
RefreshSceneList();
}
private void RefreshSceneList()
{
if (_database == null) return;
_database.scenes.Clear();
if (!Directory.Exists(BACKGROUND_PATH))
{
UnityEngine.Debug.LogWarning($"배경 폴더가 존재하지 않습니다: {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))
{
// Scene 폴더가 없으면 루트에서 .unity 파일 검색
sceneFolderPath = folderPath;
}
// .unity 파일 검색 (재귀적으로)
var sceneFiles = Directory.GetFiles(sceneFolderPath, "*.unity", SearchOption.AllDirectories);
foreach (var sceneFile in sceneFiles)
{
// SkyBox 폴더의 씬은 제외 (스카이박스 참조용)
if (sceneFile.Contains("SkyBox")) continue;
string assetPath = sceneFile.Replace("\\", "/");
string sceneName = Path.GetFileNameWithoutExtension(sceneFile);
string sceneDir = Path.GetDirectoryName(sceneFile);
// 썸네일 경로 (같은 폴더에 [씬이름].png 또는 [씬이름].jpg)
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();
_categorizedScenes = _database.GetScenesByCategory();
// 카테고리 foldout 초기화
foreach (var category in _categorizedScenes.Keys)
{
if (!_categoryFoldouts.ContainsKey(category))
{
_categoryFoldouts[category] = true;
}
}
UnityEngine.Debug.Log($"배경 씬 {_database.scenes.Count}개를 로드했습니다.");
}
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 Texture2D GetThumbnail(BackgroundSceneInfo sceneInfo)
{
if (string.IsNullOrEmpty(sceneInfo.thumbnailPath))
{
return null;
}
if (_thumbnailCache.TryGetValue(sceneInfo.thumbnailPath, out var cached))
{
return cached;
}
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(sceneInfo.thumbnailPath);
if (texture != null)
{
_thumbnailCache[sceneInfo.thumbnailPath] = texture;
}
return texture;
}
private void ClearThumbnailCache()
{
_thumbnailCache.Clear();
}
/// <summary>
/// 16:9 (또는 다른 비율) 이미지를 1:1로 중앙 크롭하여 표시
/// </summary>
private void DrawCroppedThumbnail(Rect displayRect, Texture2D texture)
{
if (texture == null) return;
float textureAspect = (float)texture.width / texture.height;
// 정사각형이거나 세로가 더 긴 경우 기존 방식
if (textureAspect <= 1.0f)
{
GUI.DrawTexture(displayRect, texture, ScaleMode.ScaleToFit);
return;
}
// 16:9 등 가로가 더 긴 경우: 중앙에서 정사각형으로 크롭
// UV 좌표 계산: 세로는 전체, 가로는 중앙 부분만
float cropWidth = (float)texture.height / texture.width; // 정사각형 비율
float uvOffsetX = (1f - cropWidth) / 2f; // 중앙 정렬
// texCoords: (x, y, width, height) - UV 공간에서의 영역
Rect texCoords = new Rect(uvOffsetX, 0f, cropWidth, 1f);
GUI.DrawTextureWithTexCoords(displayRect, texture, texCoords);
}
/// <summary>
/// 특정 썸네일 캐시 무효화 및 새로고침
/// </summary>
public void RefreshThumbnail(string thumbnailPath)
{
if (string.IsNullOrEmpty(thumbnailPath)) return;
// 캐시에서 제거
if (_thumbnailCache.ContainsKey(thumbnailPath))
{
// 기존 텍스처 언로드
var oldTexture = _thumbnailCache[thumbnailPath];
if (oldTexture != null)
{
Resources.UnloadAsset(oldTexture);
}
_thumbnailCache.Remove(thumbnailPath);
}
// 텍스처 임포터 설정으로 강제 재임포트
var importer = AssetImporter.GetAtPath(thumbnailPath);
if (importer != null)
{
importer.SaveAndReimport();
}
else
{
// 임포터가 없으면 일반 방식 사용
AssetDatabase.ImportAsset(thumbnailPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
}
// 새로 로드
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(thumbnailPath);
if (texture != null)
{
_thumbnailCache[thumbnailPath] = texture;
}
Repaint();
}
/// <summary>
/// 모든 썸네일 새로고침
/// </summary>
public void RefreshAllThumbnails()
{
// 기존 캐시의 텍스처들 언로드
foreach (var kvp in _thumbnailCache)
{
if (kvp.Value != null)
{
Resources.UnloadAsset(kvp.Value);
}
}
_thumbnailCache.Clear();
// 모든 썸네일 경로를 수집하여 강제 재임포트
if (_database != null)
{
AssetDatabase.StartAssetEditing();
try
{
foreach (var scene in _database.scenes)
{
if (!string.IsNullOrEmpty(scene.thumbnailPath))
{
var importer = AssetImporter.GetAtPath(scene.thumbnailPath);
if (importer != null)
{
// dirty flag 설정하여 재임포트 강제
EditorUtility.SetDirty(importer);
}
AssetDatabase.ImportAsset(scene.thumbnailPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
}
}
}
finally
{
AssetDatabase.StopAssetEditing();
}
}
// 에셋 데이터베이스 전체 리프레시
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
Repaint();
UnityEngine.Debug.Log("모든 썸네일이 새로고침되었습니다.");
}
private void OnGUI()
{
DrawToolbar();
DrawContent();
DrawStatusBar();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
// 새로고침 버튼
if (GUILayout.Button(EditorGUIUtility.IconContent("Refresh"), EditorStyles.toolbarButton, GUILayout.Width(30)))
{
RefreshSceneList();
RefreshAllThumbnails();
}
// 검색 필드
GUILayout.Label("검색:", GUILayout.Width(35));
_searchFilter = EditorGUILayout.TextField(_searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
if (GUILayout.Button("X", EditorStyles.toolbarButton, GUILayout.Width(20)))
{
_searchFilter = "";
GUI.FocusControl(null);
}
GUILayout.FlexibleSpace();
// 카테고리 필터
GUILayout.Label("카테고리:", GUILayout.Width(55));
var categories = new List<string> { "전체" };
if (_categorizedScenes != null)
{
categories.AddRange(_categorizedScenes.Keys.OrderBy(x => x));
}
int selectedIndex = categories.IndexOf(_selectedCategory);
if (selectedIndex < 0) selectedIndex = 0;
selectedIndex = EditorGUILayout.Popup(selectedIndex, categories.ToArray(), EditorStyles.toolbarPopup, GUILayout.Width(100));
_selectedCategory = categories[selectedIndex];
// 뷰 모드
_viewMode = GUILayout.Toolbar(_viewMode, VIEW_MODE_OPTIONS, EditorStyles.toolbarButton, GUILayout.Width(100));
// 빌드 세팅 추가 버튼
if (GUILayout.Button("빌드 세팅", EditorStyles.toolbarButton, GUILayout.Width(70)))
{
ShowBuildSettingsMenu();
}
EditorGUILayout.EndHorizontal();
}
private void ShowBuildSettingsMenu()
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("모든 배경 씬 빌드 세팅에 추가"), false, AddAllScenesToBuildSettings);
menu.AddItem(new GUIContent("빌드 세팅에 없는 씬만 추가"), false, AddMissingScenesToBuildSettings);
menu.AddSeparator("");
menu.AddItem(new GUIContent("빌드 세팅에서 배경 씬 제거"), false, RemoveAllScenesFromBuildSettings);
menu.AddSeparator("");
menu.AddItem(new GUIContent("빌드 세팅 열기"), false, () => EditorWindow.GetWindow(System.Type.GetType("UnityEditor.BuildPlayerWindow,UnityEditor")));
menu.ShowAsContext();
}
private void AddAllScenesToBuildSettings()
{
if (_database == null || _database.scenes.Count == 0)
{
EditorUtility.DisplayDialog("오류", "추가할 배경 씬이 없습니다.", "확인");
return;
}
var currentScenes = EditorBuildSettings.scenes.ToList();
int addedCount = 0;
foreach (var sceneInfo in _database.scenes)
{
// 이미 있는지 확인
bool exists = currentScenes.Any(s => s.path == sceneInfo.scenePath);
if (!exists)
{
currentScenes.Add(new EditorBuildSettingsScene(sceneInfo.scenePath, true));
addedCount++;
}
}
EditorBuildSettings.scenes = currentScenes.ToArray();
EditorUtility.DisplayDialog("완료",
$"빌드 세팅에 {addedCount}개의 배경 씬이 추가되었습니다.\n" +
$"(총 {_database.scenes.Count}개 중 {addedCount}개 새로 추가)",
"확인");
UnityEngine.Debug.Log($"빌드 세팅에 배경 씬 {addedCount}개 추가됨");
}
private void AddMissingScenesToBuildSettings()
{
if (_database == null || _database.scenes.Count == 0)
{
EditorUtility.DisplayDialog("오류", "추가할 배경 씬이 없습니다.", "확인");
return;
}
var currentScenes = EditorBuildSettings.scenes.ToList();
int addedCount = 0;
foreach (var sceneInfo in _database.scenes)
{
bool exists = currentScenes.Any(s => s.path == sceneInfo.scenePath);
if (!exists)
{
currentScenes.Add(new EditorBuildSettingsScene(sceneInfo.scenePath, true));
addedCount++;
}
}
if (addedCount == 0)
{
EditorUtility.DisplayDialog("완료", "모든 배경 씬이 이미 빌드 세팅에 있습니다.", "확인");
return;
}
EditorBuildSettings.scenes = currentScenes.ToArray();
EditorUtility.DisplayDialog("완료",
$"빌드 세팅에 {addedCount}개의 배경 씬이 추가되었습니다.",
"확인");
UnityEngine.Debug.Log($"빌드 세팅에 배경 씬 {addedCount}개 추가됨");
}
private void RemoveAllScenesFromBuildSettings()
{
if (_database == null || _database.scenes.Count == 0)
{
EditorUtility.DisplayDialog("오류", "제거할 배경 씬이 없습니다.", "확인");
return;
}
if (!EditorUtility.DisplayDialog("확인",
"빌드 세팅에서 모든 배경 씬을 제거하시겠습니까?\n" +
"이 작업은 런타임에서 배경 씬 로드를 불가능하게 만듭니다.",
"제거", "취소"))
{
return;
}
var backgroundPaths = new HashSet<string>(_database.scenes.Select(s => s.scenePath));
var currentScenes = EditorBuildSettings.scenes.ToList();
int removedCount = currentScenes.RemoveAll(s => backgroundPaths.Contains(s.path));
EditorBuildSettings.scenes = currentScenes.ToArray();
EditorUtility.DisplayDialog("완료",
$"빌드 세팅에서 {removedCount}개의 배경 씬이 제거되었습니다.",
"확인");
UnityEngine.Debug.Log($"빌드 세팅에서 배경 씬 {removedCount}개 제거됨");
}
private void DrawContent()
{
if (_database == null || _categorizedScenes == null)
{
EditorGUILayout.HelpBox("데이터베이스를 로드하는 중...", MessageType.Info);
return;
}
if (_database.scenes.Count == 0)
{
EditorGUILayout.HelpBox("배경 씬이 없습니다.\n새로고침 버튼을 눌러 씬을 검색하세요.", MessageType.Warning);
return;
}
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
var filteredScenes = GetFilteredScenes();
if (_viewMode == 0)
{
DrawGridView(filteredScenes);
}
else
{
DrawListView(filteredScenes);
}
EditorGUILayout.EndScrollView();
}
private Dictionary<string, List<BackgroundSceneInfo>> GetFilteredScenes()
{
var result = new Dictionary<string, List<BackgroundSceneInfo>>();
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.IndexOf(_searchFilter, StringComparison.OrdinalIgnoreCase) >= 0 ||
s.categoryName.IndexOf(_searchFilter, StringComparison.OrdinalIgnoreCase) >= 0
).ToList();
}
if (scenes.Count > 0)
{
result[kvp.Key] = scenes;
}
}
return result;
}
private void DrawGridView(Dictionary<string, List<BackgroundSceneInfo>> scenes)
{
float windowWidth = position.width - 30;
float itemWidth = THUMBNAIL_SIZE + GRID_PADDING;
float itemHeight = THUMBNAIL_SIZE + 30;
int columnsCount = Mathf.Max(1, (int)(windowWidth / itemWidth));
foreach (var kvp in scenes.OrderBy(x => x.Key))
{
string category = kvp.Key;
var sceneList = kvp.Value;
if (!_categoryFoldouts.ContainsKey(category))
_categoryFoldouts[category] = true;
// 카테고리 헤더
_categoryFoldouts[category] = EditorGUILayout.Foldout(_categoryFoldouts[category], $"{category} ({sceneList.Count})", true, EditorStyles.foldoutHeader);
if (!_categoryFoldouts[category]) continue;
// 그리드 영역 계산
int rowCount = Mathf.CeilToInt((float)sceneList.Count / columnsCount);
float gridHeight = rowCount * itemHeight;
// 그리드 영역 확보
Rect gridRect = GUILayoutUtility.GetRect(windowWidth, gridHeight);
gridRect.x += GRID_PADDING;
// 아이템 그리기
for (int i = 0; i < sceneList.Count; i++)
{
int row = i / columnsCount;
int col = i % columnsCount;
Rect itemRect = new Rect(
gridRect.x + col * itemWidth,
gridRect.y + row * itemHeight,
THUMBNAIL_SIZE,
itemHeight
);
DrawGridItem(sceneList[i], itemRect);
}
GUILayout.Space(GRID_PADDING);
}
}
private void DrawGridItem(BackgroundSceneInfo sceneInfo, Rect rect)
{
// 배경
bool isCurrentScene = _currentSceneName == sceneInfo.sceneName;
if (isCurrentScene)
{
EditorGUI.DrawRect(rect, SELECTED_COLOR);
}
else if (rect.Contains(Event.current.mousePosition))
{
EditorGUI.DrawRect(rect, HOVER_COLOR);
Repaint();
}
// 썸네일
var thumbnailRect = new Rect(rect.x + 2, rect.y + 2, THUMBNAIL_SIZE - 4, THUMBNAIL_SIZE - 4);
var thumbnail = GetThumbnail(sceneInfo);
if (thumbnail != null)
{
// 16:9 이미지를 1:1로 중앙 크롭하여 표시
DrawCroppedThumbnail(thumbnailRect, thumbnail);
}
else
{
EditorGUI.DrawRect(thumbnailRect, new Color(0.2f, 0.2f, 0.2f));
GUI.Label(thumbnailRect, "No\nThumbnail", new GUIStyle(EditorStyles.centeredGreyMiniLabel) { wordWrap = true, alignment = TextAnchor.MiddleCenter });
}
// 씬 이름
var labelRect = new Rect(rect.x, rect.y + THUMBNAIL_SIZE, THUMBNAIL_SIZE, 25);
GUI.Label(labelRect, sceneInfo.sceneName, new GUIStyle(EditorStyles.miniLabel) { alignment = TextAnchor.MiddleCenter, wordWrap = true, clipping = TextClipping.Clip });
// 클릭 이벤트
if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition))
{
if (Event.current.clickCount == 2)
{
LoadScene(sceneInfo);
}
else if (Event.current.button == 1)
{
ShowContextMenu(sceneInfo);
}
Event.current.Use();
}
}
private void DrawListView(Dictionary<string, List<BackgroundSceneInfo>> 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;
_categoryFoldouts[category] = EditorGUILayout.Foldout(_categoryFoldouts[category], $"{category} ({sceneList.Count})", true, EditorStyles.foldoutHeader);
if (!_categoryFoldouts[category]) continue;
EditorGUI.indentLevel++;
foreach (var sceneInfo in sceneList)
{
DrawListItem(sceneInfo);
}
EditorGUI.indentLevel--;
GUILayout.Space(5);
}
}
private void DrawListItem(BackgroundSceneInfo sceneInfo)
{
EditorGUILayout.BeginHorizontal();
bool isCurrentScene = _currentSceneName == sceneInfo.sceneName;
// 썸네일 (작게)
var thumbnail = GetThumbnail(sceneInfo);
var thumbRect = GUILayoutUtility.GetRect(40, 40, GUILayout.Width(40), GUILayout.Height(40));
if (thumbnail != null)
{
// 16:9 이미지를 1:1로 중앙 크롭하여 표시
DrawCroppedThumbnail(thumbRect, thumbnail);
}
else
{
EditorGUI.DrawRect(thumbRect, new Color(0.2f, 0.2f, 0.2f));
}
// 씬 정보
EditorGUILayout.BeginVertical();
var style = isCurrentScene ? EditorStyles.boldLabel : EditorStyles.label;
EditorGUILayout.LabelField(sceneInfo.sceneName, style);
EditorGUILayout.LabelField(sceneInfo.categoryName, EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
GUILayout.FlexibleSpace();
// 로드 버튼
if (GUILayout.Button(isCurrentScene ? "로드됨" : "로드", GUILayout.Width(60)))
{
LoadScene(sceneInfo);
}
// 메뉴 버튼
if (GUILayout.Button("...", GUILayout.Width(25)))
{
ShowContextMenu(sceneInfo);
}
EditorGUILayout.EndHorizontal();
// 구분선
var lineRect = GUILayoutUtility.GetRect(1, 1);
EditorGUI.DrawRect(lineRect, new Color(0.5f, 0.5f, 0.5f, 0.3f));
}
private void ShowContextMenu(BackgroundSceneInfo sceneInfo)
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent("로드 (Additive)"), false, () => LoadScene(sceneInfo, LoadSceneMode.Additive));
menu.AddItem(new GUIContent("로드 (Single)"), false, () => LoadScene(sceneInfo, LoadSceneMode.Single));
menu.AddSeparator("");
menu.AddItem(new GUIContent("프로젝트에서 선택"), false, () => SelectInProject(sceneInfo));
menu.AddItem(new GUIContent("폴더 열기"), false, () => RevealInFinder(sceneInfo));
menu.AddSeparator("");
menu.AddItem(new GUIContent("썸네일 생성"), false, () => CreateThumbnail(sceneInfo));
menu.ShowAsContext();
}
private void LoadScene(BackgroundSceneInfo sceneInfo, LoadSceneMode mode = LoadSceneMode.Additive)
{
if (_isLoading)
{
UnityEngine.Debug.LogWarning("이미 씬을 로드 중입니다.");
return;
}
// 현재 씬 저장 확인
if (!Application.isPlaying && EditorSceneManager.GetActiveScene().isDirty)
{
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
return;
}
}
try
{
_isLoading = true;
if (Application.isPlaying)
{
// 플레이 모드: 런타임 로더 사용
LoadSceneRuntime(sceneInfo, mode);
}
else
{
// 에디터 모드: EditorSceneManager 사용
LoadSceneEditor(sceneInfo, mode);
}
}
catch (Exception ex)
{
UnityEngine.Debug.LogError($"씬 로드 실패: {ex.Message}");
}
finally
{
_isLoading = false;
UpdateCurrentSceneStatus();
Repaint();
}
}
private void LoadSceneEditor(BackgroundSceneInfo sceneInfo, LoadSceneMode mode)
{
// 1. 같은 씬이 이미 로드되어 있는지 확인
if (IsSceneAlreadyLoaded(sceneInfo.scenePath))
{
UnityEngine.Debug.Log($"[BackgroundSceneLoader] 이미 로드된 씬입니다: {sceneInfo.sceneName}");
var existingScene = SceneManager.GetSceneByPath(sceneInfo.scenePath);
_loadedBackgroundScene = existingScene;
_currentSceneName = sceneInfo.sceneName;
return;
}
// 2. Additive 모드일 때 기존 모든 배경 씬 언로드
if (mode == LoadSceneMode.Additive)
{
UnloadAllBackgroundScenes();
}
// 3. 씬 로드
var openMode = mode == LoadSceneMode.Additive
? OpenSceneMode.Additive
: OpenSceneMode.Single;
var scene = EditorSceneManager.OpenScene(sceneInfo.scenePath, openMode);
if (mode == LoadSceneMode.Additive)
{
_loadedBackgroundScene = scene;
// 라이팅 복사 옵션이 켜져 있으면 자동으로 라이팅 복사
if (_copyLighting)
{
var activeScene = SceneManager.GetActiveScene();
if (activeScene.isLoaded && activeScene.path != scene.path)
{
// 백업이 없으면 현재 라이팅 세팅 백업
if (!_hasBackup)
{
_originalLightingSettings.Capture(activeScene);
_hasBackup = true;
UnityEngine.Debug.Log("원래 라이팅 세팅이 백업되었습니다.");
}
// 기존 씬의 Directional Light와 NiloToonOverrider 비활성화
_originalLightingSettings.DisableOriginalLighting();
CopyLightingElements(scene, activeScene);
UnityEngine.Debug.Log($"라이팅 자동 복사됨: {sceneInfo.sceneName}");
}
}
}
_currentSceneName = sceneInfo.sceneName;
UnityEngine.Debug.Log($"배경 씬 로드됨: {sceneInfo.sceneName}");
}
private void LoadSceneRuntime(BackgroundSceneInfo sceneInfo, LoadSceneMode mode)
{
// 1. 같은 씬이 이미 로드되어 있는지 확인
if (IsSceneAlreadyLoaded(sceneInfo.scenePath))
{
UnityEngine.Debug.Log($"[BackgroundSceneLoader] [PlayMode] 이미 로드된 씬입니다: {sceneInfo.sceneName}");
var existingScene = SceneManager.GetSceneByPath(sceneInfo.scenePath);
_loadedBackgroundScene = existingScene;
_currentSceneName = sceneInfo.sceneName;
return;
}
// 2. Additive 모드일 때 기존 모든 배경 씬 언로드
if (mode == LoadSceneMode.Additive)
{
UnloadAllBackgroundScenes();
}
// 3. 라이팅 백업 및 비활성화
if (mode == LoadSceneMode.Additive && _copyLighting)
{
var activeScene = SceneManager.GetActiveScene();
if (activeScene.isLoaded)
{
if (!_hasBackup)
{
_originalLightingSettings.Capture(activeScene);
_hasBackup = true;
UnityEngine.Debug.Log("[PlayMode] 원래 라이팅 세팅이 백업되었습니다.");
}
_originalLightingSettings.DisableOriginalLighting();
}
}
// 씬이 빌드 세팅에 있는지 확인
bool sceneInBuild = false;
for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
{
string scenePath = SceneUtility.GetScenePathByBuildIndex(i);
if (scenePath == sceneInfo.scenePath)
{
sceneInBuild = true;
break;
}
}
if (sceneInBuild)
{
// 빌드 세팅에 있으면 일반 로드
SceneManager.LoadSceneAsync(sceneInfo.scenePath, mode);
}
else
{
// 빌드 세팅에 없으면 에디터 전용 로드
UnityEngine.Debug.Log($"[PlayMode] 빌드 세팅에 없는 씬을 에디터 모드로 로드: {sceneInfo.sceneName}");
EditorSceneManager.LoadSceneInPlayMode(sceneInfo.scenePath, new LoadSceneParameters(mode));
}
// 씬 로드 완료 후 처리를 위한 콜백 등록
SceneManager.sceneLoaded += OnRuntimeSceneLoaded;
_currentSceneName = sceneInfo.sceneName;
}
private void OnRuntimeSceneLoaded(Scene scene, LoadSceneMode mode)
{
// 한 번만 실행
SceneManager.sceneLoaded -= OnRuntimeSceneLoaded;
// 로드된 씬이 배경 씬인지 확인
var sceneInfo = _database?.FindByPath(scene.path);
if (sceneInfo != null)
{
_loadedBackgroundScene = scene;
// 라이팅 복사
if (_copyLighting && mode == LoadSceneMode.Additive)
{
var activeScene = SceneManager.GetActiveScene();
if (activeScene.isLoaded && activeScene.path != scene.path)
{
CopyLightingElements(scene, activeScene);
UnityEngine.Debug.Log($"[PlayMode] 라이팅 자동 복사됨: {sceneInfo.sceneName}");
}
}
UnityEngine.Debug.Log($"[PlayMode] 배경 씬 로드됨: {sceneInfo.sceneName}");
}
Repaint();
}
private void UpdateCurrentSceneStatus()
{
if (_database == null) return;
// 현재 로드된 모든 배경 씬 찾기
var loadedBackgroundScenes = new List<Scene>();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (!scene.isLoaded) continue;
var sceneInfo = _database.FindByPath(scene.path);
if (sceneInfo != null)
{
loadedBackgroundScenes.Add(scene);
}
}
// 배경 씬이 여러 개 로드된 경우 경고 (첫 번째만 유지)
if (loadedBackgroundScenes.Count > 1)
{
UnityEngine.Debug.LogWarning($"[BackgroundSceneLoader] 중복 배경 씬 감지: {loadedBackgroundScenes.Count}개. 첫 번째만 유지합니다.");
// 첫 번째를 제외하고 모두 언로드
for (int i = 1; i < loadedBackgroundScenes.Count; i++)
{
var duplicateScene = loadedBackgroundScenes[i];
UnityEngine.Debug.Log($"[BackgroundSceneLoader] 중복 씬 언로드: {duplicateScene.name}");
if (Application.isPlaying)
{
SceneManager.UnloadSceneAsync(duplicateScene);
}
else
{
EditorSceneManager.CloseScene(duplicateScene, true);
}
}
}
// 상태 업데이트
if (loadedBackgroundScenes.Count > 0)
{
var firstScene = loadedBackgroundScenes[0];
_loadedBackgroundScene = firstScene;
_currentSceneName = _database.FindByPath(firstScene.path)?.sceneName ?? firstScene.name;
}
else
{
_loadedBackgroundScene = null;
_currentSceneName = "없음";
}
}
/// <summary>
/// 현재 로드된 모든 배경 씬 언로드
/// </summary>
private void UnloadAllBackgroundScenes()
{
if (_database == null) return;
var scenesToUnload = new List<Scene>();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (!scene.isLoaded) continue;
var sceneInfo = _database.FindByPath(scene.path);
if (sceneInfo != null)
{
scenesToUnload.Add(scene);
}
}
foreach (var scene in scenesToUnload)
{
UnityEngine.Debug.Log($"[BackgroundSceneLoader] 기존 배경 씬 언로드: {scene.name}");
if (Application.isPlaying)
{
SceneManager.UnloadSceneAsync(scene);
}
else
{
EditorSceneManager.CloseScene(scene, true);
}
}
}
/// <summary>
/// 특정 씬이 이미 로드되어 있는지 확인
/// </summary>
private bool IsSceneAlreadyLoaded(string scenePath)
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (scene.path == scenePath && scene.isLoaded)
{
return true;
}
}
return false;
}
private void SelectInProject(BackgroundSceneInfo sceneInfo)
{
var asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(sceneInfo.scenePath);
if (asset != null)
{
Selection.activeObject = asset;
EditorGUIUtility.PingObject(asset);
}
}
private void RevealInFinder(BackgroundSceneInfo sceneInfo)
{
EditorUtility.RevealInFinder(sceneInfo.scenePath);
}
private void CreateThumbnail(BackgroundSceneInfo sceneInfo)
{
// 씬 로드 후 Game 뷰 캡처
EditorUtility.DisplayDialog("썸네일 생성",
"씬을 로드한 후 Game 뷰에서 원하는 앵글을 설정하고\n" +
"Streamingle > Capture Thumbnail 메뉴를 사용하세요.",
"확인");
}
private void DrawStatusBar()
{
// 라이팅 옵션 펼침
_showLightingOptions = EditorGUILayout.Foldout(_showLightingOptions, "라이팅 복사 옵션", true);
if (_showLightingOptions)
{
EditorGUI.indentLevel++;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
_copyLighting = EditorGUILayout.Toggle("라이팅 복사 활성화", _copyLighting);
GUI.enabled = _copyLighting;
EditorGUI.indentLevel++;
_copySkybox = EditorGUILayout.Toggle("Skybox", _copySkybox);
_copyAmbient = EditorGUILayout.Toggle("Ambient (환경광)", _copyAmbient);
_copyFog = EditorGUILayout.Toggle("Fog (안개)", _copyFog);
EditorGUI.indentLevel--;
GUI.enabled = true;
// 자동 복원 옵션
_autoRestoreOnUnload = EditorGUILayout.Toggle("배경 언로드 시 자동 복원", _autoRestoreOnUnload);
// 백업 상태 표시
if (_hasBackup)
{
EditorGUILayout.HelpBox("원래 라이팅 세팅이 백업되었습니다. '원래대로 복원' 버튼으로 복원할 수 있습니다.", MessageType.Info);
}
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("현재 배경에서 라이팅 복사"))
{
CopyLightingFromCurrentBackground();
}
GUI.enabled = _hasBackup;
if (GUILayout.Button("원래대로 복원"))
{
RestoreOriginalLighting();
}
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
EditorGUI.indentLevel--;
}
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
// 현재 씬 상태
GUILayout.Label($"현재 배경: {_currentSceneName}");
GUILayout.FlexibleSpace();
// 언로드 버튼
GUI.enabled = _loadedBackgroundScene.HasValue && !_isLoading;
if (GUILayout.Button("배경 언로드", EditorStyles.toolbarButton))
{
UnloadCurrentBackground();
}
GUI.enabled = true;
// 로딩 상태
if (_isLoading)
{
GUILayout.Label("로드 중...");
}
EditorGUILayout.EndHorizontal();
}
private void CopyLightingFromCurrentBackground()
{
if (!_loadedBackgroundScene.HasValue || !_loadedBackgroundScene.Value.isLoaded)
{
EditorUtility.DisplayDialog("오류", "배경 씬이 로드되지 않았습니다.", "확인");
return;
}
var activeScene = SceneManager.GetActiveScene();
if (!activeScene.isLoaded)
{
EditorUtility.DisplayDialog("오류", "활성 씬이 없습니다.", "확인");
return;
}
// 같은 씬인지 확인
if (activeScene.path == _loadedBackgroundScene.Value.path)
{
EditorUtility.DisplayDialog("오류", "배경 씬과 활성 씬이 동일합니다.", "확인");
return;
}
// 백업이 없으면 현재 라이팅 세팅 백업
if (!_hasBackup)
{
_originalLightingSettings.Capture(activeScene);
_hasBackup = true;
UnityEngine.Debug.Log("원래 라이팅 세팅이 백업되었습니다.");
}
CopyLightingElements(_loadedBackgroundScene.Value, activeScene);
EditorUtility.DisplayDialog("완료", "라이팅 세팅이 복사되었습니다.\n'원래대로 복원' 버튼으로 원래 세팅을 복원할 수 있습니다.", "확인");
}
private void CopyLightingElements(Scene sourceScene, Scene targetScene)
{
var sourceRoots = sourceScene.GetRootGameObjects();
// RenderSettings 복사를 위해 소스 씬을 임시로 활성 씬으로 설정
var previousActiveScene = SceneManager.GetActiveScene();
SceneManager.SetActiveScene(sourceScene);
// Skybox 복사
if (_copySkybox)
{
// 먼저 RenderSettings.skybox 확인
if (RenderSettings.skybox != null)
{
var sourceSkybox = RenderSettings.skybox;
SceneManager.SetActiveScene(targetScene);
RenderSettings.skybox = sourceSkybox;
SceneManager.SetActiveScene(sourceScene);
UnityEngine.Debug.Log($"Skybox 복사됨: {sourceSkybox.name}");
}
else
{
// 카메라의 Skybox 컴포넌트 확인 (fallback)
foreach (var root in sourceRoots)
{
var cameras = root.GetComponentsInChildren<Camera>(true);
foreach (var cam in cameras)
{
var skyboxComp = cam.GetComponent<Skybox>();
if (skyboxComp != null && skyboxComp.material != null)
{
SceneManager.SetActiveScene(targetScene);
RenderSettings.skybox = skyboxComp.material;
SceneManager.SetActiveScene(sourceScene);
UnityEngine.Debug.Log($"Skybox 복사됨 (카메라): {skyboxComp.material.name}");
break;
}
}
}
}
}
// Ambient 복사
if (_copyAmbient)
{
var ambientMode = RenderSettings.ambientMode;
var ambientSkyColor = RenderSettings.ambientSkyColor;
var ambientEquatorColor = RenderSettings.ambientEquatorColor;
var ambientGroundColor = RenderSettings.ambientGroundColor;
var ambientLight = RenderSettings.ambientLight;
var ambientIntensity = RenderSettings.ambientIntensity;
var subtractiveShadowColor = RenderSettings.subtractiveShadowColor;
SceneManager.SetActiveScene(targetScene);
RenderSettings.ambientMode = ambientMode;
RenderSettings.ambientSkyColor = ambientSkyColor;
RenderSettings.ambientEquatorColor = ambientEquatorColor;
RenderSettings.ambientGroundColor = ambientGroundColor;
RenderSettings.ambientLight = ambientLight;
RenderSettings.ambientIntensity = ambientIntensity;
RenderSettings.subtractiveShadowColor = subtractiveShadowColor;
SceneManager.SetActiveScene(sourceScene);
UnityEngine.Debug.Log("Ambient 설정 복사됨");
}
// Fog 복사
if (_copyFog)
{
var fog = RenderSettings.fog;
var fogColor = RenderSettings.fogColor;
var fogMode = RenderSettings.fogMode;
var fogDensity = RenderSettings.fogDensity;
var fogStartDistance = RenderSettings.fogStartDistance;
var fogEndDistance = RenderSettings.fogEndDistance;
SceneManager.SetActiveScene(targetScene);
RenderSettings.fog = fog;
RenderSettings.fogColor = fogColor;
RenderSettings.fogMode = fogMode;
RenderSettings.fogDensity = fogDensity;
RenderSettings.fogStartDistance = fogStartDistance;
RenderSettings.fogEndDistance = fogEndDistance;
SceneManager.SetActiveScene(sourceScene);
UnityEngine.Debug.Log($"Fog 설정 복사됨 (활성화: {fog})");
}
// 원래 활성 씬으로 복원
SceneManager.SetActiveScene(previousActiveScene);
UnityEngine.Debug.Log($"라이팅 복사됨: {sourceScene.name} → {targetScene.name}");
}
private void RestoreOriginalLighting()
{
if (!_hasBackup)
{
EditorUtility.DisplayDialog("오류", "백업된 라이팅 세팅이 없습니다.", "확인");
return;
}
var activeScene = SceneManager.GetActiveScene();
if (!activeScene.isLoaded)
{
EditorUtility.DisplayDialog("오류", "활성 씬이 없습니다.", "확인");
return;
}
// 원래 RenderSettings 복원
_originalLightingSettings.Restore();
_hasBackup = false;
EditorSceneManager.MarkSceneDirty(activeScene);
UnityEngine.Debug.Log("원래 라이팅 세팅이 복원되었습니다.");
EditorUtility.DisplayDialog("완료", "원래 라이팅 세팅이 복원되었습니다.", "확인");
}
private void UnloadCurrentBackground()
{
if (!_loadedBackgroundScene.HasValue) return;
try
{
// 백업이 있으면 Directional Light와 NiloToonOverrider 항상 복원
if (_hasBackup)
{
// 자동 복원 옵션이 켜져 있으면 RenderSettings도 복원
if (_autoRestoreOnUnload)
{
_originalLightingSettings.Restore();
UnityEngine.Debug.Log("배경 언로드: 원래 라이팅 세팅이 자동 복원되었습니다.");
}
else
{
// RenderSettings는 복원하지 않고 라이팅 컴포넌트만 복원
_originalLightingSettings.RestoreOriginalLighting();
}
_hasBackup = false;
// MarkSceneDirty는 플레이 모드에서 사용할 수 없음
if (!Application.isPlaying)
{
var activeScene = SceneManager.GetActiveScene();
if (activeScene.isLoaded)
{
EditorSceneManager.MarkSceneDirty(activeScene);
}
}
}
if (Application.isPlaying)
{
SceneManager.UnloadSceneAsync(_loadedBackgroundScene.Value);
}
else
{
EditorSceneManager.CloseScene(_loadedBackgroundScene.Value, true);
}
_loadedBackgroundScene = null;
_currentSceneName = "없음";
Repaint();
}
catch (Exception ex)
{
UnityEngine.Debug.LogError($"씬 언로드 실패: {ex.Message}");
}
}
}
}