using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; // 게임 클리어 시 처리: // - NPC(오브젝트)들을 지정 위치로 재배치 // - 대화 존(DialogRegion)들을 활성/비활성 전환 // 리듬게임의 On Cleared 같은 UnityEvent에 OnGameClear()를 연결하면 한 번에 처리된다. public class GameClear : MonoBehaviour { [SerializeField] private RoomClearGateController _clearGate; // 옮길 오브젝트 ↔ 목적지(빈 Transform) 한 쌍 [System.Serializable] public struct Relocation { public GameObject Target; // 옮길 오브젝트 (NPC 등) public Transform Destination; // 옮길 위치/회전 기준 [Tooltip("체크하면 NavMeshAgent의 Base Offset을 아래 값으로 변경한다 (높이 조절용). NavMeshAgent가 없으면 무시됨.")] public bool OverrideBaseOffset; // NavMeshAgent의 baseOffset을 바꿀지 여부 public float BaseOffset; // 적용할 baseOffset 값 } // 클리어 시 적용할 존 상태 한 쌍 [System.Serializable] public struct ZoneState { public DialogRegion Zone; // 대상 존 public bool Active; // 이 상태로 전환 } [Header("NPC 재배치")] [SerializeField] private List _relocations = new(); [Header("대화 존 상태")] [SerializeField] private List _zoneStates = new(); // ── 클리어 시 한 번에 (UnityEvent 연결용) ───────────────────── public void OnGameClear() { if(_clearGate != null) { //_clearGate.OpenClearGate(); _clearGate.MarkRoomCleared(); } Relocate(); ApplyZoneStates(); SetClearDialogParameter(); } // ── NPC 재배치 ─────────────────────────────────────────────── // 리스트의 각 오브젝트를 지정 위치(위치+회전)로 이동. public void Relocate() { foreach (var r in _relocations) Relocate(r.Target, r.Destination, r.OverrideBaseOffset, r.BaseOffset); } // 단일 오브젝트 재배치(baseOffset 변경 없음). 기존 호출부 호환용 오버로드. public void Relocate(GameObject target, Transform destination) { Relocate(target, destination, false, 0f); } // 단일 오브젝트 재배치. NavMeshAgent가 있으면 Warp로 옮겨야 경로/내부상태가 안 깨진다. // NavMeshAgent는 (NavMesh 표면 높이 + baseOffset)으로 y가 강제되므로, // 높이를 바꾸려면 destination.y가 아니라 baseOffset을 조정해야 한다. public void Relocate(GameObject target, Transform destination, bool overrideBaseOffset, float baseOffset) { if (target == null || destination == null) return; // 따라다니던 중이면 멈춰서 재배치 위치에 머물게 (다시 플레이어를 향해 가지 않도록) if (target.TryGetComponent(out FollowObject follow) && follow.FollowEnabled) follow.DisableFollow(); if (target.TryGetComponent(out NavMeshAgent agent) && agent.isOnNavMesh) { // 높이 조절: Warp 전에 baseOffset을 바꿔야 옮긴 위치에 바로 반영된다. if (overrideBaseOffset) agent.baseOffset = baseOffset; Debug.Log($"엔드포지션 : {destination.position}"); agent.Warp(destination.position); target.transform.rotation = destination.rotation; Debug.Log($"이동한포지션 : {target.transform.position}"); } else { target.transform.SetPositionAndRotation(destination.position, destination.rotation); } } // ── 대화 존 활성/비활성 ────────────────────────────────────── // 인스펙터에 설정한 _zoneStates를 한 번에 적용. public void ApplyZoneStates() { foreach (var z in _zoneStates) SetZoneActive(z.Zone, z.Active); } // 특정 존을 활성/비활성 (존 오브젝트째로 토글 → 트리거도 같이 꺼짐). public void SetZoneActive(DialogRegion zone, bool active) { if (zone != null) zone.gameObject.SetActive(active); } public void SetClearDialogParameter() { string sceneName1 = "블랙잭"; string sceneCode1 = "blackjack"; string sceneName2 = "캣룸"; string sceneCode2 = "CatsRoom"; if (RandomSceneRouteManager.Instance != null) { RandomSceneRouteManager.Instance.PrepareNextSceneChoices(); sceneName1 = RandomSceneRouteManager.Instance.GetNextSceneName1(); sceneCode1 = RandomSceneRouteManager.Instance.GetNextSceneCode1(); sceneName2 = RandomSceneRouteManager.Instance.GetNextSceneName2(); sceneCode2 = RandomSceneRouteManager.Instance.GetNextSceneCode2(); } else { Debug.LogWarning("RandomSceneRouteManager가 없어서 테스트용 선택지를 사용합니다."); } DialogVariables.Set("SpaceSceneName1", sceneName1); DialogVariables.Set("SpaceSceneCode1", sceneCode1); DialogVariables.Set("SpaceSceneName2", sceneName2); DialogVariables.Set("SpaceSceneCode2", sceneCode2); if (_clearGate != null) { _clearGate.SetDoorChoices(sceneCode1, sceneCode2); } else { Debug.LogWarning("GameClear에 RoomClearGateController가 연결되지 않았습니다."); } Debug.Log($"선택지 세팅 완료: {sceneName1}/{sceneCode1}, {sceneName2}/{sceneCode2}"); } }