using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class CatNoteSpawner : MonoBehaviour { [Header("References")] [SerializeField] private RectTransform noteRoot; [SerializeField] private RectTransform noteSpawnPoint; [SerializeField] private RectTransform hitZone; [Header("Note Prefabs")] [SerializeField] private RhythmNote pawNotePrefab; [SerializeField] private RhythmNote fishNotePrefab; [SerializeField] private RhythmNote musicNotePrefab; [Header("Spawn Settings")] [SerializeField] private float normalSpawnInterval = 0.9f; [SerializeField] private float easySpawnInterval = 1.2f; [SerializeField] private float normalNoteSpeed = 360f; [SerializeField] private float easyNoteSpeed = 300f; [SerializeField] private float firstSpawnDelay = 0.5f; [SerializeField] private float autoMissDistance = 90f; [SerializeField] private int maxActiveNotes = 12; [Header("Debug")] [SerializeField] private bool showDebugLog = false; private readonly List activeNotes = new List(); private Coroutine spawnRoutine; private bool spawning; private bool easyMode; public event Action NoteAutoMissed; public bool IsSpawning => spawning; public IReadOnlyList ActiveNotes => activeNotes; public void StartSpawn(bool useEasyMode) { StopSpawn(false); easyMode = useEasyMode; spawning = true; spawnRoutine = StartCoroutine(SpawnRoutine()); } public void StopSpawn(bool clearNotes = true) { spawning = false; if (spawnRoutine != null) { StopCoroutine(spawnRoutine); spawnRoutine = null; } if (clearNotes) ClearNotes(); } private IEnumerator SpawnRoutine() { yield return new WaitForSeconds(firstSpawnDelay); while (spawning) { if (activeNotes.Count < maxActiveNotes) SpawnNote(); yield return new WaitForSeconds(GetSpawnInterval()); } } public RhythmNote SpawnNote() { RhythmNote prefab = PickNotePrefab(); if (prefab == null) { Debug.LogWarning("[CatNoteSpawner] 사용할 노트 프리팹이 없습니다."); return null; } if (noteRoot == null || noteSpawnPoint == null || hitZone == null) { Debug.LogWarning("[CatNoteSpawner] NoteRoot, NoteSpawnPoint, HitZone 중 연결되지 않은 값이 있습니다."); return null; } RhythmNote note = Instantiate(prefab, noteRoot); RectTransform noteRect = note.RectTransform; noteRect.localScale = Vector3.one; noteRect.localRotation = Quaternion.identity; noteRect.localPosition = noteRoot.InverseTransformPoint(noteSpawnPoint.position); float hitX = noteRoot.InverseTransformPoint(hitZone.position).x; note.Initialize(GetNoteSpeed(), hitX, autoMissDistance); note.PassedHitZone += OnNotePassedHitZone; activeNotes.Add(note); if (showDebugLog) Debug.Log("[CatNoteSpawner] Note Spawned"); return note; } private RhythmNote PickNotePrefab() { List availablePrefabs = new List(); if (pawNotePrefab != null) availablePrefabs.Add(pawNotePrefab); if (fishNotePrefab != null) availablePrefabs.Add(fishNotePrefab); if (musicNotePrefab != null) availablePrefabs.Add(musicNotePrefab); if (availablePrefabs.Count == 0) return null; return availablePrefabs[UnityEngine.Random.Range(0, availablePrefabs.Count)]; } private float GetSpawnInterval() { return easyMode ? easySpawnInterval : normalSpawnInterval; } private float GetNoteSpeed() { return easyMode ? easyNoteSpeed : normalNoteSpeed; } private void OnNotePassedHitZone(RhythmNote note) { NoteAutoMissed?.Invoke(note); RemoveNote(note, true); } public RhythmNote GetClosestNote() { CleanupNullNotes(); RhythmNote closest = null; float closestDistance = float.MaxValue; for (int i = 0; i < activeNotes.Count; i++) { RhythmNote note = activeNotes[i]; if (note == null || note.IsJudged) continue; float distance = note.GetDistanceToHitZone(); if (distance < closestDistance) { closest = note; closestDistance = distance; } } return closest; } public void RemoveNote(RhythmNote note, bool destroyObject) { if (note == null) return; note.PassedHitZone -= OnNotePassedHitZone; activeNotes.Remove(note); if (destroyObject) note.Remove(); else note.MarkJudged(); } public void ClearNotes() { for (int i = activeNotes.Count - 1; i >= 0; i--) { RhythmNote note = activeNotes[i]; if (note == null) continue; note.PassedHitZone -= OnNotePassedHitZone; note.Remove(); } activeNotes.Clear(); } private void CleanupNullNotes() { for (int i = activeNotes.Count - 1; i >= 0; i--) { if (activeNotes[i] == null) activeNotes.RemoveAt(i); } } }