using System; using UnityEngine; public class GameManager : MonoBehaviour,ISceneInitializable { public static GameManager Instance; //현재 플레이어가 위치한 구역 (구역 상태의 단일 진실 출처) public Zone CurrentZone { get; private set; } = Zone.None; //구역이 바뀌면 발생. 사운드 등 여러 시스템이 구독해서 반응한다. public event Action OnZoneChanged; private void Awake() { if (Instance == null) { Instance = this; //만들어진 자신을 인스턴스로 설정 DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); //이미 인스턴스가 있으면 자신을 파괴 } } //구역 변경 요청 (ZoneTrigger 등에서 호출) public void SetZone(Zone zone) { if (zone == CurrentZone) return; //같은 구역이면 무시 CurrentZone = zone; OnZoneChanged?.Invoke(zone); //구독자(SoundManager 등)에게 통지 } public void OnSceneLoaded() { ResolveZoneAtSpawn(); } //씬 로드 직후, 플레이어가 서 있는 위치에서 ZoneTrigger를 직접 조회해 구역을 확정한다. //None을 깔고 OnTriggerEnter를 기다리는 방식의 BGM 글리치/누락(스폰 즉시 트리거 안인 경우)을 피하기 위함. private void ResolveZoneAtSpawn() { Zone resolved = Zone.None; var player = GameObject.FindGameObjectWithTag("Player"); if (player != null) { //콜라이더는 Awake/OnEnable에서 이미 물리씬에 등록되므로 첫 물리 스텝 전에도 즉시 조회 가능 Collider[] hits = Physics.OverlapSphere( player.transform.position, 0.1f, ~0, QueryTriggerInteraction.Collide); foreach (var hit in hits) { if (hit.TryGetComponent(out var trigger)) { resolved = trigger.Zone; break; } } } SetZone(resolved); //이전 구역과 같으면 dedup되어 BGM이 끊김 없이 이어진다 } //게임 종료 (에디터에서는 플레이 모드 중지, 빌드에서는 앱 종료) public void QuitGame() { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } }