using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을 using UnityEngine.SceneManagement; // 씬 관리 기능을 불러올거에요 -> SceneManager를 using UnityEngine.UI; // UI 기능을 불러올거에요 -> Image 컴포넌트 탐색용 using TMPro; // TMP 기능을 불러올거에요 -> TextMeshProUGUI 탐색용 using System.Collections; // 코루틴을 사용할거에요 -> IEnumerator를 // ══════════════════════════════════════════════════════════════════ // DungeonSceneSetup — 던전/보스 씬 초기 설정 관리자 // // [역할] // 던전 또는 보스 씬이 로드될 때 필요한 초기 설정을 수행합니다. // - DontDestroyOnLoad 플레이어가 정상 존재하는지 확인 // - DontDestroyOnLoad 카메라/UI가 정상 존재하는지 확인 // - 에디터에서 씬을 직접 Play할 때 플레이어/카메라/UI 프리팹 자동 생성 // - 던전 전용 환경 설정 (안개, 라이팅, BGM 등) // // [메인 씬 캐릭터 변경 사항 자동 반영 원리] // ★ 핵심: 플레이어 오브젝트 자체가 DontDestroyOnLoad로 살아있기 때문에 // 메인 씬에서 장비, 스탯, 스킬 등을 바꾸면 그 오브젝트가 그대로 // 던전 씬으로 넘어옵니다. 별도 저장/로드 로직이 필요 없습니다. // 카메라와 UI도 동일하게 DontDestroyOnLoad로 유지됩니다. // // [사용법] // 1. 던전/보스 씬에 빈 오브젝트 "DungeonManager" 생성 // 2. 이 스크립트 부착 // 3. fallbackPlayerPrefab에 Player 프리팹 연결 // 4. fallbackCameraPrefab에 Main Camera 프리팹 연결 (또는 Virtual Camera) // 5. fallbackUIPrefabs 배열에 UI Canvas 프리팹들 연결 // 6. PlayerSpawnPoint도 씬에 배치 (스폰 위치 지정) // ══════════════════════════════════════════════════════════════════ public class DungeonSceneSetup : MonoBehaviour // 클래스를 정의할거에요 -> 던전 씬 초기화 관리자를 { // ───────────────────────────────────────────────────────────── // Inspector 설정 — 플레이어 // ───────────────────────────────────────────────────────────── [Header("=== 플레이어 폴백 (에디터 전용) ===")] [Tooltip("에디터에서 씬을 직접 Play할 때 사용할 Player 프리팹\n" + "정상적인 게임 흐름(메인→던전)에서는 사용되지 않습니다")] [SerializeField] private GameObject fallbackPlayerPrefab; // 변수를 선언할거에요 -> 폴백 플레이어 프리팹을 [Header("=== 플레이어 태그 ===")] [SerializeField] private string playerTag = "Player"; // 변수를 선언할거에요 -> 플레이어 태그를 // ───────────────────────────────────────────────────────────── // Inspector 설정 — 카메라 // ───────────────────────────────────────────────────────────── [Header("=== 카메라 폴백 (에디터 전용) ===")] [Tooltip("에디터에서 씬을 직접 Play할 때 사용할 Main Camera 프리팹\n" + "※ Main Camera 태그가 설정된 카메라 프리팹을 연결하세요")] [SerializeField] private GameObject fallbackCameraPrefab; // 변수를 선언할거에요 -> 폴백 메인 카메라 프리팹을 [Tooltip("에디터에서 씬을 직접 Play할 때 사용할 Virtual Camera 프리팹\n" + "※ Cinemachine Virtual Camera 프리팹을 연결하세요")] [SerializeField] private GameObject fallbackVirtualCameraPrefab; // 변수를 선언할거에요 -> 폴백 가상 카메라 프리팹을 // ───────────────────────────────────────────────────────────── // Inspector 설정 — UI // ───────────────────────────────────────────────────────────── [Header("=== UI 폴백 (에디터 전용) ===")] [Tooltip("에디터에서 씬을 직접 Play할 때 사용할 UI Canvas 프리팹들\n" + "예: Player Canvas, StopCanvas, SettingsCanvas, DeathUICanvas 등\n" + "정상적인 게임 흐름에서는 DontDestroyOnLoad로 넘어오므로 사용되지 않습니다")] [SerializeField] private GameObject[] fallbackUIPrefabs; // 변수를 선언할거에요 -> 폴백 UI 프리팹 배열을 // ───────────────────────────────────────────────────────────── // Inspector 설정 — 씬 복귀 // ───────────────────────────────────────────────────────────── [Header("=== 씬 복귀 설정 ===")] [Tooltip("플레이어가 돌아갈 메인 씬 이름")] [SerializeField] private string mainSceneName = "MainScene"; // 변수를 선언할거에요 -> 메인 씬 이름을 // ───────────────────────────────────────────────────────────── // Inspector 설정 — 던전 환경 // ───────────────────────────────────────────────────────────── [Header("=== 던전 환경 (선택) ===")] [Tooltip("던전 씬 로드 시 안개 활성화 여부")] [SerializeField] private bool enableFog = true; // 변수를 선언할거에요 -> 안개 활성화 여부를 [SerializeField] private Color fogColor = new Color(0.05f, 0.03f, 0.08f); // 변수를 선언할거에요 -> 안개 색상을 (어두운 보라) [SerializeField] private float fogDensity = 0.02f; // 변수를 선언할거에요 -> 안개 밀도를 [Tooltip("던전 씬 진입 시 재생할 앰비언트 라이트 색상")] [SerializeField] private Color ambientColor = new Color(0.1f, 0.08f, 0.15f); // 변수를 선언할거에요 -> 앰비언트 색상을 // ───────────────────────────────────────────────────────────── // 내부 상태 // ───────────────────────────────────────────────────────────── private GameObject _cachedPlayer; // 변수를 선언할거에요 -> 찾은 플레이어 캐시를 private bool _isDirectPlay = false; // 변수를 선언할거에요 -> 직접 Play 모드인지 여부를 // ───────────────────────────────────────────────────────────── // 초기화 — 씬 로드 시 자동 실행 // ───────────────────────────────────────────────────────────── private void Awake() // Awake에서 실행할거에요 -> 가장 먼저 초기화를 { EnsurePlayerExists(); // 실행할거에요 -> 플레이어 존재 확인을 // 직접 Play일 때만 카메라/UI 폴백 생성 (메인씬에서 넘어왔으면 이미 DontDestroyOnLoad에 있음) if (_isDirectPlay) // 조건이 맞으면 실행할거에요 -> 직접 Play 모드면 { EnsureCameraExists(); // 실행할거에요 -> 카메라 존재 확인을 EnsureUIExists(); // 실행할거에요 -> UI 존재 확인을 } ApplyDungeonEnvironment(); // 실행할거에요 -> 던전 환경 설정을 } /// /// Start에서 실행 — Awake에서 모든 오브젝트 생성 후, /// UI와 카메라의 플레이어 참조를 자동으로 연결합니다. /// Awake → 오브젝트 생성 / Start → 참조 연결 (타이밍 안전) /// private void Start() // Start에서 실행할거에요 -> 참조 연결을 (Awake 이후) { if (_isDirectPlay && _cachedPlayer != null) // 조건이 맞으면 실행할거에요 -> 직접 Play이고 플레이어가 있으면 { LinkAllUIToPlayer(); // 실행할거에요 -> 모든 UI에 플레이어 참조 연결을 LinkCanvasRenderCameras(); // 실행할거에요 -> Canvas의 Render Camera 연결을 StartCoroutine(DelayedUIRefresh()); // 코루틴을 시작할거에요 -> 1프레임 뒤 UI 강제 갱신을 (타이밍 안전장치) } } /// /// 모든 Start()가 실행된 뒤 UI를 한번 더 갱신합니다. /// PlayerHealth.Start()에서 CurrentHP가 초기화되기 전에 /// LinkAllUIToPlayer()가 먼저 실행될 수 있어서 필요합니다. /// private IEnumerator DelayedUIRefresh() // 코루틴을 정의할거에요 -> 지연 UI 갱신을 { yield return null; // 기다릴거에요 -> 1프레임 (모든 Start() 완료 보장) if (_cachedPlayer == null) yield break; // 조건이 맞으면 중단할거에요 -> 플레이어 없으면 // ── HP바 강제 갱신 ── PlayerHealth playerHealth = _cachedPlayer.GetComponent(); // 가져올거에요 -> 플레이어 체력을 if (playerHealth != null) // 조건이 맞으면 실행할거에요 -> 체력 스크립트가 있으면 { playerHealth.RefreshHealthUI(); // 실행할거에요 -> HP 이벤트를 다시 발생시켜 UI 갱신 Debug.Log("[DungeonSetup] 지연 갱신: HP UI 강제 Refresh 완료"); // 로그를 찍을거에요 } // ── 레벨 시스템 강제 갱신 ── PlayerLevelSystem levelSys = FindObjectOfType(true); // 찾을거에요 -> 레벨 시스템을 if (levelSys != null) // 조건이 맞으면 실행할거에요 -> 레벨 시스템이 있으면 { levelSys.ForceRefreshUI(); // 실행할거에요 -> 레벨 UI 강제 갱신을 Debug.Log("[DungeonSetup] 지연 갱신: 레벨 UI 강제 Refresh 완료"); // 로그를 찍을거에요 } // ── HPUibar 강제 갱신 (텍스트 포함) ── HPUibar hpBar = FindObjectOfType(true); // 찾을거에요 -> HP바를 if (hpBar != null) // 조건이 맞으면 실행할거에요 -> HP바가 있으면 { hpBar.ForceRefresh(); // 실행할거에요 -> HP바 강제 갱신을 Debug.Log("[DungeonSetup] 지연 갱신: HPUibar 강제 Refresh 완료"); // 로그를 찍을거에요 } Debug.Log("[DungeonSetup] ★ 지연 UI 갱신 완료 (모든 Start() 이후)"); // 로그를 찍을거에요 } // ───────────────────────────────────────────────────────────── // 플레이어 폴백 생성 // ───────────────────────────────────────────────────────────── /// /// DontDestroyOnLoad 플레이어가 존재하는지 확인합니다. /// 에디터에서 던전 씬을 직접 Play할 때만 폴백 프리팹을 생성합니다. /// 정상 흐름(메인→던전)에서는 이미 플레이어가 DontDestroyOnLoad에 있습니다. /// private void EnsurePlayerExists() // 함수를 정의할거에요 -> 플레이어 존재 보장을 { // 1단계: 태그로 기존 플레이어 검색 _cachedPlayer = GameObject.FindGameObjectWithTag(playerTag); // 찾을거에요 -> 플레이어를 if (_cachedPlayer != null) // 조건이 맞으면 실행할거에요 -> 이미 플레이어가 있으면 { _isDirectPlay = false; // 설정할거에요 -> 메인 씬에서 정상 진입한 것으로 판단 Debug.Log($"[DungeonSetup] 플레이어 발견: {_cachedPlayer.name} — 메인 씬 상태 그대로 유지됩니다"); // 로그를 찍을거에요 return; // 중단할거에요 -> 정상 상태이므로 추가 작업 불필요 } // 2단계: 플레이어가 없음 (에디터에서 직접 Play한 경우) _isDirectPlay = true; // 설정할거에요 -> 직접 Play 모드로 판단 Debug.LogWarning("[DungeonSetup] 플레이어를 찾을 수 없습니다! 에디터 테스트 모드로 폴백 생성합니다."); // 경고를 찍을거에요 if (fallbackPlayerPrefab != null) // 조건이 맞으면 실행할거에요 -> 폴백 프리팹이 설정되어 있으면 { // 폴백 플레이어 생성 (PlayerSpawnPoint 위치 또는 원점) Vector3 spawnPos = Vector3.zero; // 기본 위치를 원점으로 설정할거에요 Quaternion spawnRot = Quaternion.identity; // 기본 회전을 무회전으로 설정할거에요 // PlayerSpawnPoint가 있으면 그 위치 사용 PlayerSpawnPoint spawnPoint = FindObjectOfType(); // 찾을거에요 -> 스폰 포인트를 if (spawnPoint != null) // 조건이 맞으면 실행할거에요 -> 스폰 포인트가 있으면 { spawnPos = spawnPoint.transform.position; // 설정할거에요 -> 스폰 위치로 spawnRot = spawnPoint.transform.rotation; // 설정할거에요 -> 스폰 회전으로 } _cachedPlayer = Instantiate(fallbackPlayerPrefab, spawnPos, spawnRot); // 생성할거에요 -> 폴백 플레이어를 _cachedPlayer.name = "Player (Fallback)"; // 이름을 설정할거에요 -> 폴백임을 명시 Debug.Log($"[DungeonSetup] 폴백 플레이어 생성 완료: {spawnPos}"); // 로그를 찍을거에요 } else // 폴백 프리팹도 없으면 { Debug.LogError("[DungeonSetup] fallbackPlayerPrefab이 설정되지 않았습니다! " + "Inspector에서 Player 프리팹을 연결하거나, 메인 씬에서 정상 진입하세요."); // 에러를 찍을거에요 } } // ───────────────────────────────────────────────────────────── // 카메라 폴백 생성 // ───────────────────────────────────────────────────────────── /// /// 카메라가 존재하는지 확인합니다. /// 에디터에서 씬을 직접 Play할 때만 폴백 카메라를 생성합니다. /// 메인 카메라 → 가상 카메라 순서로 생성합니다. /// private void EnsureCameraExists() // 함수를 정의할거에요 -> 카메라 존재 보장을 { // ── 1. 메인 카메라 확인 및 폴백 생성 ── if (Camera.main == null) // 조건이 맞으면 실행할거에요 -> 메인 카메라가 없으면 { if (fallbackCameraPrefab != null) // 조건이 맞으면 실행할거에요 -> 폴백 프리팹이 있으면 { GameObject cam = Instantiate(fallbackCameraPrefab); // 생성할거에요 -> 폴백 메인 카메라를 cam.name = "Main Camera (Fallback)"; // 이름을 설정할거에요 -> 폴백임을 명시 Debug.Log("[DungeonSetup] 폴백 메인 카메라 생성 완료"); // 로그를 찍을거에요 } else // 프리팹이 없으면 { Debug.LogError("[DungeonSetup] fallbackCameraPrefab이 설정되지 않았습니다! " + "Inspector에서 Main Camera 프리팹을 연결하세요."); // 에러를 찍을거에요 } } else // 카메라가 이미 있으면 { Debug.Log("[DungeonSetup] 메인 카메라 이미 존재 — 스킵"); // 로그를 찍을거에요 } // ── 2. 가상 카메라 (Cinemachine) 확인 및 폴백 생성 ── if (fallbackVirtualCameraPrefab != null) // 조건이 맞으면 실행할거에요 -> 가상 카메라 프리팹이 설정되어 있으면 { // Cinemachine.CinemachineVirtualCamera 타입으로 검색 var existingVCam = FindObjectOfType(); // 찾을거에요 -> 기존 가상 카메라를 if (existingVCam == null) // 조건이 맞으면 실행할거에요 -> 가상 카메라가 없으면 { GameObject vCam = Instantiate(fallbackVirtualCameraPrefab); // 생성할거에요 -> 폴백 가상 카메라를 vCam.name = "Virtual Camera (Fallback)"; // 이름을 설정할거에요 -> 폴백임을 명시 // ★ 폴백 가상 카메라에 플레이어 자동 연결 LinkVirtualCameraToPlayer(vCam); // 실행할거에요 -> 카메라와 플레이어 연결을 Debug.Log("[DungeonSetup] 폴백 가상 카메라 생성 완료"); // 로그를 찍을거에요 } else // 이미 있으면 { Debug.Log("[DungeonSetup] 가상 카메라 이미 존재 — 스킵"); // 로그를 찍을거에요 } } } /// /// 폴백 가상 카메라의 Follow/LookAt을 플레이어에 자동 연결합니다. /// 프리팹에서 새로 생성하면 참조가 끊어지기 때문에 수동으로 연결해야 합니다. /// private void LinkVirtualCameraToPlayer(GameObject vCamObj) // 함수를 정의할거에요 -> 가상 카메라 타겟 연결을 { if (_cachedPlayer == null) // 조건이 맞으면 실행할거에요 -> 플레이어가 없으면 { Debug.LogWarning("[DungeonSetup] 플레이어가 없어서 카메라 Follow 연결을 건너뜁니다."); // 경고를 찍을거에요 return; // 중단할거에요 } // 가상 카메라 컴포넌트 가져오기 var vCam = vCamObj.GetComponent(); // 가져올거에요 -> CinemachineVirtualCamera를 if (vCam == null) // 조건이 맞으면 실행할거에요 -> 컴포넌트가 없으면 { Debug.LogWarning("[DungeonSetup] VirtualCamera 컴포넌트를 찾을 수 없습니다."); // 경고를 찍을거에요 return; // 중단할거에요 } // Follow와 LookAt에 플레이어 Transform 연결 vCam.Follow = _cachedPlayer.transform; // 연결할거에요 -> Follow 타겟을 플레이어로 vCam.LookAt = _cachedPlayer.transform; // 연결할거에요 -> LookAt 타겟을 플레이어로 Debug.Log($"[DungeonSetup] 가상 카메라 Follow/LookAt → {_cachedPlayer.name} 연결 완료"); // 로그를 찍을거에요 } // ───────────────────────────────────────────────────────────── // UI 폴백 생성 // ───────────────────────────────────────────────────────────── /// /// UI Canvas들이 존재하는지 확인합니다. /// 에디터에서 씬을 직접 Play할 때만 폴백 UI를 생성합니다. /// fallbackUIPrefabs 배열에 등록된 프리팹을 순차 생성합니다. /// private void EnsureUIExists() // 함수를 정의할거에요 -> UI 존재 보장을 { if (fallbackUIPrefabs == null || fallbackUIPrefabs.Length == 0) // 조건이 맞으면 실행할거에요 -> 폴백 UI가 없으면 { Debug.LogWarning("[DungeonSetup] fallbackUIPrefabs가 비어있습니다. UI 없이 진행합니다."); // 경고를 찍을거에요 return; // 중단할거에요 } for (int i = 0; i < fallbackUIPrefabs.Length; i++) // 반복할거에요 -> 각 UI 프리팹마다 { if (fallbackUIPrefabs[i] == null) // 조건이 맞으면 실행할거에요 -> 빈 슬롯이면 { Debug.LogWarning($"[DungeonSetup] fallbackUIPrefabs[{i}]가 비어있습니다 — 건너뜁니다."); // 경고를 찍을거에요 continue; // 다음으로 넘어갈거에요 } // 같은 이름의 오브젝트가 이미 있는지 확인 (DontDestroyOnLoad에서 넘어왔을 수 있음) string prefabName = fallbackUIPrefabs[i].name; // 가져올거에요 -> 프리팹 이름을 GameObject existing = GameObject.Find(prefabName); // 찾을거에요 -> 같은 이름의 기존 오브젝트를 if (existing != null) // 조건이 맞으면 실행할거에요 -> 이미 존재하면 { Debug.Log($"[DungeonSetup] UI '{prefabName}' 이미 존재 — 스킵"); // 로그를 찍을거에요 continue; // 다음으로 넘어갈거에요 } // 폴백 UI 생성 GameObject uiInstance = Instantiate(fallbackUIPrefabs[i]); // 생성할거에요 -> 폴백 UI를 uiInstance.name = $"{prefabName} (Fallback)"; // 이름을 설정할거에요 -> 폴백임을 명시 Debug.Log($"[DungeonSetup] 폴백 UI 생성 완료: {prefabName}"); // 로그를 찍을거에요 } } // ───────────────────────────────────────────────────────────── // UI ↔ Player 자동 연결 (폴백 모드 전용) // ───────────────────────────────────────────────────────────── /// /// 씬에 존재하는 모든 UI 스크립트의 플레이어 참조를 자동 연결합니다. /// 폴백으로 프리팹을 새로 생성하면 씬 참조가 끊기기 때문에 필요합니다. /// 대상: HPUibar, PlayerStatsUI, ArrowRangeUI, PlayerLevelSystem /// private void LinkAllUIToPlayer() // 함수를 정의할거에요 -> 모든 UI에 플레이어 연결을 { if (_cachedPlayer == null) // 조건이 맞으면 실행할거에요 -> 플레이어가 없으면 { Debug.LogWarning("[DungeonSetup] 플레이어가 없어서 UI 연결을 건너뜁니다."); // 경고를 찍을거에요 return; // 중단할거에요 } // 플레이어의 핵심 컴포넌트들을 미리 캐싱 (GetComponent 최소화) Stats playerStats = _cachedPlayer.GetComponent(); // 가져올거에요 -> 플레이어 스탯을 PlayerHealth playerHealth = _cachedPlayer.GetComponent(); // 가져올거에요 -> 플레이어 체력을 PlayerAttack playerAttack = _cachedPlayer.GetComponent(); // 가져올거에요 -> 플레이어 공격을 // ── 1. HPUibar — targetObject 연결 ── HPUibar hpBar = FindObjectOfType(true); // 찾을거에요 -> HP UI바를 (비활성 포함) if (hpBar != null) // 조건이 맞으면 실행할거에요 -> HP바가 있으면 { hpBar.SetTargetObject(_cachedPlayer); // 연결할거에요 -> HP바의 타겟을 플레이어로 Debug.Log("[DungeonSetup] HPUibar → Player 연결 완료"); // 로그를 찍을거에요 } // ── 2. PlayerStatsUI — playerStats 연결 ── PlayerStatsUI statsUI = FindObjectOfType(true); // 찾을거에요 -> 스탯 UI를 (비활성 포함) if (statsUI != null && playerStats != null) // 조건이 맞으면 실행할거에요 -> 둘 다 있으면 { statsUI.SetPlayerStats(playerStats); // 연결할거에요 -> 스탯 UI에 플레이어 스탯을 Debug.Log("[DungeonSetup] PlayerStatsUI → Stats 연결 완료"); // 로그를 찍을거에요 } // ── 3. ArrowRangeUI (CrossHairUI) — attackScript 연결 ── ArrowRangeUI crosshair = FindObjectOfType(true); // 찾을거에요 -> 조준선 UI를 (비활성 포함) if (crosshair != null && playerAttack != null) // 조건이 맞으면 실행할거에요 -> 둘 다 있으면 { crosshair.SetAttackScript(playerAttack); // 연결할거에요 -> 조준선에 공격 스크립트를 Debug.Log("[DungeonSetup] ArrowRangeUI → PlayerAttack 연결 완료"); // 로그를 찍을거에요 } // ── 4. PlayerLevelSystem — stats, pHealth 연결 ── PlayerLevelSystem levelSys = FindObjectOfType(true); // 찾을거에요 -> 레벨 시스템을 (비활성 포함) if (levelSys != null) // 조건이 맞으면 실행할거에요 -> 레벨 시스템이 있으면 { levelSys.SetPlayerReferences(playerStats, playerHealth); // 연결할거에요 -> 레벨 시스템에 플레이어 참조를 Debug.Log("[DungeonSetup] PlayerLevelSystem → Stats, PlayerHealth 연결 완료"); // 로그를 찍을거에요 } Debug.Log("[DungeonSetup] ★ 모든 UI ↔ Player 연결 완료"); // 로그를 찍을거에요 } /// /// Screen Space - Camera 모드의 Canvas에 Render Camera를 자동 연결합니다. /// 폴백으로 생성하면 Camera 참조가 None이 되기 때문에 필요합니다. /// private void LinkCanvasRenderCameras() // 함수를 정의할거에요 -> Canvas 카메라 연결을 { Camera mainCam = Camera.main; // 가져올거에요 -> 현재 메인 카메라를 if (mainCam == null) // 조건이 맞으면 실행할거에요 -> 메인 카메라가 없으면 { Debug.LogWarning("[DungeonSetup] 메인 카메라가 없어서 Canvas 카메라 연결을 건너뜁니다."); // 경고를 찍을거에요 return; // 중단할거에요 } // 씬에 있는 모든 Canvas 검색 (비활성 포함) Canvas[] allCanvas = FindObjectsOfType(true); // 찾을거에요 -> 모든 Canvas를 for (int i = 0; i < allCanvas.Length; i++) // 반복할거에요 -> 각 Canvas마다 { Canvas canvas = allCanvas[i]; // 가져올거에요 -> 현재 Canvas를 // Screen Space - Camera 모드인데 카메라가 없으면 연결 if (canvas.renderMode == RenderMode.ScreenSpaceCamera && canvas.worldCamera == null) // 조건이 맞으면 -> 카메라 모드인데 카메라 없으면 { canvas.worldCamera = mainCam; // 연결할거에요 -> 메인 카메라를 Render Camera로 Debug.Log($"[DungeonSetup] Canvas '{canvas.name}' → Render Camera 연결 완료"); // 로그를 찍을거에요 } } } // ───────────────────────────────────────────────────────────── // 던전 환경 설정 // ───────────────────────────────────────────────────────────── /// /// 던전 환경 설정을 적용합니다 (안개, 앰비언트 라이트 등) /// private void ApplyDungeonEnvironment() // 함수를 정의할거에요 -> 던전 환경을 { // 안개 설정 if (enableFog) // 조건이 맞으면 실행할거에요 -> 안개를 켤 거면 { RenderSettings.fog = true; // 활성화할거에요 -> 안개를 RenderSettings.fogColor = fogColor; // 설정할거에요 -> 안개 색상을 RenderSettings.fogDensity = fogDensity; // 설정할거에요 -> 안개 밀도를 RenderSettings.fogMode = FogMode.Exponential; // 설정할거에요 -> 안개 모드를 지수형으로 } // 앰비언트 라이트 설정 RenderSettings.ambientLight = ambientColor; // 설정할거에요 -> 앰비언트 색상을 Debug.Log("[DungeonSetup] 던전 환경 설정 적용 완료"); // 로그를 찍을거에요 } // ───────────────────────────────────────────────────────────── // 공개 API — 다른 스크립트에서 호출 가능 // ───────────────────────────────────────────────────────────── /// /// 메인 씬으로 복귀합니다. /// 던전 클리어 시 또는 복귀 포탈에서 호출하세요. /// 플레이어 상태는 DontDestroyOnLoad이므로 자동 유지됩니다. /// public void ReturnToMainScene() // 함수를 정의할거에요 -> 메인 씬 복귀를 { if (string.IsNullOrEmpty(mainSceneName)) // 조건이 맞으면 실행할거에요 -> 씬 이름이 없으면 { Debug.LogError("[DungeonSetup] mainSceneName이 설정되지 않았습니다!"); // 에러를 찍을거에요 return; // 중단할거에요 } Debug.Log($"[DungeonSetup] 메인 씬으로 복귀: {mainSceneName}"); // 로그를 찍을거에요 if (SceneLoader.Instance != null) // SceneLoader가 있으면 페이드 전환 { SceneLoader.Instance.LoadSceneWithFade(mainSceneName); // 페이드 로드 } else // 없으면 직접 로드 { SceneManager.LoadScene(mainSceneName); // 직접 로드 } } // ───────────────────────────────────────────────────────────── // Gizmo — 씬 뷰에서 던전 매니저 위치 표시 // ───────────────────────────────────────────────────────────── private void OnDrawGizmos() // 씬 뷰에서 그릴거에요 -> 매니저 위치를 { Gizmos.color = new Color(0.6f, 0.2f, 0.8f, 0.5f); // 보라색 반투명 Gizmos.DrawWireSphere(transform.position, 1f); // 원 그리기 #if UNITY_EDITOR UnityEditor.Handles.Label(transform.position + Vector3.up * 1.5f, // 라벨 위치 "Dungeon Manager", // 라벨 텍스트 new GUIStyle { // 스타일 normal = { textColor = new Color(0.8f, 0.4f, 1f) }, // 보라 텍스트 fontSize = 12, // 글자 크기 fontStyle = FontStyle.Bold // 볼드 }); #endif } }