using UnityEngine; /// /// 하이브리드 몬스터 스포너 (생성은 스포너 기준, 생존은 몬스터 기준) /// - Birth: 스포너 기준 거리로 생성 /// - Life & Hibernate: 몬스터 기준 거리로 최적화 /// - Wake Up: 범위 안으로 복귀 시 재활성화 /// public class MonsterSpawner : MonoBehaviour { [Header("=== 생성 설정 (Birth - 스포너 기준) ===")] [Tooltip("스포너로부터 이 거리 안에 플레이어가 들어오면 몬스터 생성")] [SerializeField] private float spawnRange = 25f; [Tooltip("몬스터가 죽은 후 재생성까지 대기 시간")] [SerializeField] private float respawnCooldown = 3f; [Tooltip("오브젝트 풀에서 가져올 몬스터 태그")] [SerializeField] private string mobTag = "NormalMob"; [Header("=== 최적화 설정 (Life & Hibernate - 몬스터 기준) ===")] [Tooltip("몬스터 본체로부터 이 거리를 벗어나면 강제 동면 (전투 중이어도!)")] [SerializeField] private float optimizationRange = 60f; // 내부 상태 private GameObject _myMonster; private MonsterClass _monsterScript; private Transform _player; private float _nextSpawnTime; private void Start() { FindPlayer(); } private void FindPlayer() { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) _player = playerObj.transform; } private void Update() { if (_player == null) { FindPlayer(); return; } // 몬스터가 없거나 죽었으면 -> Birth if (_myMonster == null || (_monsterScript != null && _monsterScript.IsDead)) { HandleBirth(); } // 몬스터가 살아있으면 -> Life & Hibernate else { HandleLifeAndHibernate(); } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 🐣 BIRTH (생성) - 스포너 기준 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void HandleBirth() { // ⭐ 스포너 <-> 플레이어 거리 float distanceFromSpawner = Vector3.Distance(transform.position, _player.position); if (distanceFromSpawner <= spawnRange && Time.time >= _nextSpawnTime) { SpawnMonster(); } } private void SpawnMonster() { _myMonster = GenericObjectPool.Instance.SpawnFromPool(mobTag, transform.position, transform.rotation); if (_myMonster != null) { _monsterScript = _myMonster.GetComponent(); if (_monsterScript != null) _monsterScript.ResetStats(); } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 💤 LIFE & HIBERNATE (생존/동면) - 몬스터 기준 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void HandleLifeAndHibernate() { if (_myMonster == null) { _monsterScript = null; return; } // ⭐ 몬스터 본체 <-> 플레이어 거리 float distanceFromMonster = Vector3.Distance(_myMonster.transform.position, _player.position); if (distanceFromMonster > optimizationRange) { // 😴 HIBERNATE (동면) HibernateMonster(); } else { // 👁 WAKE UP (기상) WakeUpMonster(); } } private void HibernateMonster() { if (_myMonster.activeSelf) { _myMonster.SetActive(false); } } private void WakeUpMonster() { if (!_myMonster.activeSelf) { _myMonster.SetActive(true); if (_monsterScript != null) { // ⭐ AI 복구 (전투 중이었다면 추적 재개) _monsterScript.Reactivate(); } } } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 💀 DEATH (사망 처리 및 쿨타임) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ /// /// 몬스터가 죽었을 때 호출 (쿨타임 적용) /// public void NotifyMonsterDead() { // 쿨타임 계산: 현재 시간 + 대기 시간 _nextSpawnTime = Time.time + respawnCooldown; // 몬스터 참조를 비워서 Update에서 다시 HandleBirth()로 넘어가게 함 _myMonster = null; _monsterScript = null; } // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Gizmos // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ private void OnDrawGizmosSelected() { // 스포너 생성 범위 (빨간색) Gizmos.color = Color.red; Gizmos.DrawWireSphere(transform.position, spawnRange); // 몬스터 최적화 범위 (파란색) if (_myMonster != null) { Gizmos.color = Color.blue; Gizmos.DrawWireSphere(_myMonster.transform.position, optimizationRange); } } }