using System.Collections; using UnityEngine; public enum NoteSegment { None, Start, Middle, End } [RequireComponent(typeof(HoldNoteController))] public class HoldNote : BaseNote { private KeyCode keyToPress = KeyCode.None; private string noteColor = string.Empty; private string noteID = string.Empty; private NoteSegment segment = NoteSegment.None; private float hitTime = 0f; private float releaseTime = 0f; private bool hasReleased = true; private bool hasEnteredLine = false; private Coroutine autoReturnCoroutine; private HoldNoteController controller; public int id; public int trackIndex; public float speed; public float startTime; public float delay; public bool isEnd; public float scheduledEndTime; public string color; public KeyCode key; public string type; // "start", "middle", or "end" private void Awake() { controller = GetComponent(); } private void OnEnable() { ResetState(); } public void Setup(int id, int trackIndex, float speed, float time, float delay, bool isEnd, float scheduledEnd, string color, KeyCode key, string type) { this.id = id; this.trackIndex = trackIndex; this.speed = speed; this.startTime = time; this.delay = delay; this.isEnd = isEnd; this.scheduledEndTime = scheduledEnd; this.color = color; this.key = key; this.type = type; this.noteID = id.ToString(); this.keyToPress = key; this.noteColor = color; this.hitTime = time + delay; this.segment = (delay == 0f) ? NoteSegment.Start : (isEnd ? NoteSegment.End : NoteSegment.Middle); this.hasEnteredLine = false; this.hasReleased = false; JudgeManager.Instance?.RegisterNoteReleased(noteID, false); if (segment == NoteSegment.Start) JudgeManager.Instance?.RegisterStartJudged(noteID, false); if (segment == NoteSegment.End) JudgeManager.Instance?.RegisterScheduledEndTime(noteID, scheduledEndTime); controller?.SetSpeed(speed); controller?.SetSegmentDelay(delay); } private void Update() { if (segment == NoteSegment.Start && Input.GetKeyDown(keyToPress)) { if (hasEnteredLine) HandleStart(); } else if (segment == NoteSegment.End && Input.GetKeyUp(keyToPress)) { HandleEnd(); } if (segment == NoteSegment.Middle && hasEnteredLine) { if (JudgeManager.Instance.IsStartJudged(noteID) && !JudgeManager.Instance.HasNoteReleased(noteID) && Input.GetKey(keyToPress)) { // 不再立即回收,由协程等待 hitTime 再回收 } } } private void OnTriggerEnter2D(Collider2D collision) { if (!collision.CompareTag("JudgmentLine")) return; if (segment == NoteSegment.Start || segment == NoteSegment.Middle) { hasEnteredLine = true; } if (segment == NoteSegment.Middle) { Debug.Log($"[HoldNote] Middle段进入判定线: color={noteColor}, isStartJudged={JudgeManager.Instance.IsStartJudged(noteID)}, hasReleased={JudgeManager.Instance.HasNoteReleased(noteID)}, isKeyDown={Input.GetKey(keyToPress)}"); if (autoReturnCoroutine != null) StopCoroutine(autoReturnCoroutine); autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck()); } if (segment == NoteSegment.End) { if (autoReturnCoroutine != null) StopCoroutine(autoReturnCoroutine); autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck()); } } private void OnTriggerExit2D(Collider2D collision) { if (!collision.CompareTag("JudgmentLine")) return; if (segment == NoteSegment.Start) { if (!JudgeManager.Instance.IsStartJudged(noteID)) { ReturnToPool(); } } if (segment == NoteSegment.Middle) { if (!JudgeManager.Instance.IsStartJudged(noteID) || JudgeManager.Instance.HasNoteReleased(noteID)) { Debug.Log($"[HoldNote] Middle段离开判定线强制回收: {noteColor}"); ReturnToPool(); } } else if (segment == NoteSegment.End) { if (!JudgeManager.Instance.IsStartJudged(noteID) || JudgeManager.Instance.HasNoteReleased(noteID)) { ReturnToPool(); } } } private IEnumerator DelayedAutoReturnCheck() { yield return null; bool isStartJudgedBefore = JudgeManager.Instance.IsStartJudged(noteID); bool isKeyHeldNow = Input.GetKey(keyToPress); Debug.Log($"[HoldNote] 延迟检查开始: noteID={noteID}, color={noteColor}, isStartJudged={isStartJudgedBefore}, isKeyHeld={isKeyHeldNow}"); if (!isStartJudgedBefore && isKeyHeldNow) { Debug.Log($"[HoldNote] 延迟回补 Start 判定触发: {noteColor}"); HandleStart(); } bool isStartJudged = JudgeManager.Instance.IsStartJudged(noteID); bool hasReleased = JudgeManager.Instance.HasNoteReleased(noteID); bool isKeyHeld = Input.GetKey(keyToPress); Debug.Log($"[HoldNote] 延迟确认状态: isStartJudged={isStartJudged}, hasReleased={hasReleased}, isKeyHeld={isKeyHeld}"); if (segment == NoteSegment.Middle && isStartJudged && isKeyHeld) { float waitTime = Mathf.Max(0f, hitTime - Time.time); Debug.Log($"[HoldNote] Middle段通过延迟确认,准备等待到 hitTime 回收: {noteColor}, 剩余时间={waitTime:F2}s"); yield return new WaitForSeconds(waitTime); if (Input.GetKey(keyToPress) && !JudgeManager.Instance.HasNoteReleased(noteID)) { Debug.Log($"[HoldNote] Middle段在 hitTime 达成条件,回收: {noteColor}"); ReturnToPool(); } else { Debug.Log($"[HoldNote] Middle段在 hitTime 条件不符,不回收: {noteColor}"); } } else if (segment == NoteSegment.End && !hasReleased) { Debug.Log($"[HoldNote] End段延迟检查后仍未释放: {noteColor}"); } else { Debug.Log($"[HoldNote] DelayedAutoReturnCheck 无需处理: segment={segment}, color={noteColor}"); } } private void HandleStart() { float pressTime = Time.time; float offset = Mathf.Abs(pressTime - hitTime); if (offset < 0.1f) { Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}"); JudgeManager.Instance.RegisterStartJudged(noteID, true); } else { Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}"); } StartCoroutine(DelayedReturn()); } private IEnumerator DelayedReturn() { yield return new WaitForSeconds(0.05f); ReturnToPool(); } private void HandleEnd() { releaseTime = Time.time; hasReleased = true; JudgeManager.Instance.RegisterNoteReleased(noteID, true); float diff = Mathf.Abs(releaseTime - scheduledEndTime); string result; if (releaseTime >= scheduledEndTime) { result = "Perfect"; } else if (diff < 0.1f) { result = "Perfect"; } else if (diff < 0.5f) { result = "Great"; } else { result = "Good"; } Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})"); ReturnToPool(); } private void OnDisable() { if (autoReturnCoroutine != null) { StopCoroutine(autoReturnCoroutine); autoReturnCoroutine = null; } if (segment == NoteSegment.End && !hasReleased && !JudgeManager.Instance.HasNoteReleased(noteID) && keyToPress != KeyCode.None && !Input.GetKey(keyToPress)) { hasReleased = true; JudgeManager.Instance.RegisterNoteReleased(noteID, true); Debug.Log($"[HoldNote] OnDisable补偿KeyUp: {noteColor}"); } controller?.StopMovement(); } private void ReturnToPool() { if (!gameObject.activeSelf) return; if (segment == NoteSegment.Start && !hasEnteredLine) { Debug.LogWarning($"[HoldNote] 阻止回收尚未进入判定线的 Start段: {noteColor}"); return; } gameObject.SetActive(false); if (segment == NoteSegment.Start) NotePool.Instance.ReturnStartNote(gameObject, noteColor); else NotePool.Instance.ReturnHoldNoteSegment(gameObject, noteColor); } public void ResetState() { hasReleased = false; segment = NoteSegment.None; keyToPress = KeyCode.None; noteColor = string.Empty; noteID = string.Empty; hitTime = 0f; scheduledEndTime = 0f; hasEnteredLine = false; if (autoReturnCoroutine != null) { StopCoroutine(autoReturnCoroutine); autoReturnCoroutine = null; } controller?.StopMovement(); } }