1646 lines
61 KiB
C#
1646 lines
61 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scene 또는 scene 폴더를 실제 대소문자로 찾기
|
|
/// Windows는 대소문자를 구분하지 않지만, Git/Linux는 구분하므로 정확한 이름이 필요
|
|
/// </summary>
|
|
private string FindSceneFolderWithCorrectCase(string parentFolder)
|
|
{
|
|
var subDirs = Directory.GetDirectories(parentFolder);
|
|
foreach (var dir in subDirs)
|
|
{
|
|
string dirName = Path.GetFileName(dir);
|
|
if (string.Equals(dirName, "Scene", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// 실제 폴더 이름 반환 (scene 또는 Scene)
|
|
return dir;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
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 = FindSceneFolderWithCorrectCase(folderPath);
|
|
|
|
if (sceneFolderPath == null)
|
|
{
|
|
// 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}");
|
|
}
|
|
}
|
|
}
|
|
}
|