using UnityEngine; // 유니티 기능을 불러올거에요 -> UnityEngine을 using System.Collections; // 코루틴을 사용할거에요 -> System.Collections를 using System.Collections.Generic; // 리스트를 사용할거에요 -> System.Collections.Generic을 public class MonsterSpwaner : MonoBehaviour // 클래스를 선언할거에요 -> 몬스터 스포너를 { [Header("스폰 설정")] [SerializeField] private List monsterPrefabs; // 리스트를 선언할거에요 -> 스폰할 몬스터 프리팹들을 [SerializeField] private List spawnPoints; // 리스트를 선언할거에요 -> 스폰 위치들을 [Tooltip("이 스포너에서 동시에 유지할 최대 몬스터 수")] [SerializeField] private int maxMonsters = 5; // 변수를 선언할거에요 -> 이 스포너가 유지할 최대 마릿수를 [Tooltip("몬스터가 죽은 뒤 몇 초 후에 다시 스폰할지")] [SerializeField] private float respawnDelay = 5f; // 변수를 선언할거에요 -> 죽고 나서 재스폰까지 대기 시간을 // 이 스포너가 관리하는 몬스터 목록 // null이면 → 죽어서 비어있는 슬롯 (재스폰 대상) private List _managedMonsters; // 리스트를 선언할거에요 -> 슬롯별 몬스터 오브젝트를 // ───────────────────────────────────────────────────────── private void Start() // 함수를 실행할거에요 -> 시작 시 { // 슬롯 리스트를 maxMonsters 크기로 초기화 (전부 null = 비어있음) _managedMonsters = new List(); // 초기화할거에요 -> 슬롯 목록을 for (int i = 0; i < maxMonsters; i++) // 반복할거에요 -> 최대 마릿수만큼 { _managedMonsters.Add(null); // 추가할거에요 -> 빈 슬롯을 } // 처음에 모든 슬롯 즉시 스폰 for (int i = 0; i < maxMonsters; i++) // 반복할거에요 -> 슬롯마다 { SpawnAtSlot(i); // 실행할거에요 -> 즉시 스폰을 } } // ───────────────────────────────────────────────────────── // 매 프레임 슬롯 감시 // ───────────────────────────────────────────────────────── private void Update() // 함수를 실행할거에요 -> 매 프레임 { for (int i = 0; i < _managedMonsters.Count; i++) // 반복할거에요 -> 모든 슬롯을 { GameObject mob = _managedMonsters[i]; // 가져올거에요 -> 슬롯의 몬스터를 // 슬롯이 비어있거나(null), 오브젝트가 비활성화됐으면 → 죽은 것으로 판단 if (mob == null || !mob.activeInHierarchy) // 조건이 맞으면 실행할거에요 -> 슬롯이 비었으면 { _managedMonsters[i] = null; // 초기화할거에요 -> 슬롯을 명확하게 null로 StartCoroutine(RespawnAfterDelay(i)); // 시작할거에요 -> 재스폰 대기 코루틴을 _managedMonsters[i] = new GameObject("RespawnPending"); // 임시 표시할거에요 -> 중복 코루틴 방지용으로 // 실제 스폰은 RespawnAfterDelay에서 처리 } } } // ───────────────────────────────────────────────────────── // 재스폰 대기 코루틴 // ───────────────────────────────────────────────────────── private IEnumerator RespawnAfterDelay(int slotIndex) // 코루틴을 정의할거에요 -> n초 대기 후 재스폰을 { yield return new WaitForSeconds(respawnDelay); // 기다릴거에요 -> 설정한 대기 시간만큼 SpawnAtSlot(slotIndex); // 실행할거에요 -> 해당 슬롯에 스폰을 } // ───────────────────────────────────────────────────────── // 실제 스폰 // ───────────────────────────────────────────────────────── private void SpawnAtSlot(int slotIndex) // 함수를 선언할거에요 -> 특정 슬롯에 몬스터를 스폰하는 { if (monsterPrefabs.Count == 0 || spawnPoints.Count == 0) return; // 중단할거에요 -> 설정이 없으면 // 랜덤 프리팹, 랜덤 스폰 위치 선택 GameObject prefab = monsterPrefabs[Random.Range(0, monsterPrefabs.Count)]; // 뽑을거에요 -> 랜덤 몬스터 프리팹을 Transform point = spawnPoints[Random.Range(0, spawnPoints.Count)]; // 뽑을거에요 -> 랜덤 스폰 위치를 GameObject mob = Instantiate(prefab, point.position, point.rotation); // 생성할거에요 -> 몬스터를 // OnEnable() → ResetStats() 자동 호출되므로 별도 초기화 불필요 _managedMonsters[slotIndex] = mob; // 저장할거에요 -> 슬롯에 몬스터 오브젝트를 Debug.Log($"[Spawner] 슬롯 {slotIndex} 스폰 완료: {mob.name}"); // 로그를 찍을거에요 } }