using UnityEngine; public class Note : BaseNote { private KeyCode keyToPress; private string noteColor; private AnimationController anim; private NoteController controller; private bool isJudged = false; [Header("判定区间配置")] public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值 private void Awake() { anim = GetComponent(); controller = GetComponent(); } public void Setup(KeyCode key, int trackIndex, float speed, float hitTime, string color, NoteJudgeConfig judgeConfig) { keyToPress = key; noteColor = color; TrackIndex = trackIndex; Speed = speed; this.hitTime = hitTime; isJudged = false; this.judgeConfig = judgeConfig; if (controller != null) { controller.SetSpeed(speed); } InputManager.OnKeyPressed += HandlePress; // 判定区间配置检查 if (judgeConfig == null) Debug.LogError($"NoteJudgeConfig is null! 判定区间配置未传入!track={trackIndex}"); else Debug.Log($"NoteJudgeConfig: perfect={judgeConfig.perfectRange}, great={judgeConfig.greatRange}, good={judgeConfig.goodRange}, miss={judgeConfig.missRange}"); } private void OnDestroy() { InputManager.OnKeyPressed -= HandlePress; } private void HandlePress(KeyCode key) { // 只判定正确轨道且在判定区 if (isJudged || key != keyToPress || (controller != null && !controller.IsInJudgeZone())) return; float timeDifference = Mathf.Abs(Time.time - hitTime); string judgeResult = null; if (judgeConfig != null) { if (timeDifference <= judgeConfig.perfectRange) { Debug.Log($"{keyToPress}: Perfect"); judgeResult = "Perfect"; } else if (timeDifference <= judgeConfig.greatRange) { Debug.Log($"{keyToPress}: Great"); judgeResult = "Great"; } else if (timeDifference <= judgeConfig.goodRange) { Debug.Log($"{keyToPress}: Good"); judgeResult = "Good"; } else if (timeDifference <= judgeConfig.missRange) { Debug.Log($"{keyToPress}: Miss"); judgeResult = "Miss"; // Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well } else { Debug.Log($"{keyToPress}: Miss"); judgeResult = "Miss"; // Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well } } else { // fallback timing if (timeDifference <= 0.08f) { Debug.Log($"{keyToPress}: Perfect"); judgeResult = "Perfect"; } else if (timeDifference <= 0.15f) { Debug.Log($"{keyToPress}: Great"); judgeResult = "Great"; } else { Debug.Log($"{keyToPress}: Miss"); judgeResult = "Miss"; // Do not return here - let shared post-judge logic run so NotifyNoteHit is invoked for Miss as well } } // 显示判定信息 if (!string.IsNullOrEmpty(judgeResult)) { InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult); JudgeSoundManager.Instance?.PlayJudgeSound(judgeResult); teamUIController.Instance?.OnJudgeResult(judgeResult); // 更新combo计数 // Calculate score addition via AllyCombatant and add to per-track pm sums try { var allyGo = GameObject.Find($"ally_0{TrackIndex + 1}"); if (allyGo != null) { var ally = allyGo.GetComponent(); if (ally != null) { int added = ally.AddScoreForJudge(judgeResult); ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added); } } } catch (System.Exception ex) { Debug.LogWarning($"[Note] Failed to add per-track score: {ex}"); } // Notify SkillBuilder about this note hit so OnNoteHit skills may trigger (tap notes) try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, judgeResult, SkillDefinition.NoteTypeTrigger.Tap); } catch { } } Judge(); } public bool IsJudged() { return isJudged; } public void Judge() { if (isJudged) return; isJudged = true; if (controller != null) { controller.StopMovement(); controller.PlayHitEffect(); } ReturnToPool(); if (anim !=null) { anim.PlayDestroyAnimation(noteColor); } } public void JudgeMiss() { if (isJudged) return; isJudged = true; Debug.Log($"{keyToPress} Miss"); // Ensure UI shows Miss and combo is updated when a note auto-misses InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); JudgeSoundManager.Instance?.PlayJudgeSound("Miss"); teamUIController.Instance?.OnJudgeResult("Miss"); // Add score for Miss as well (some systems may give 0) try { var allyGo = GameObject.Find($"ally_0{TrackIndex + 1}"); if (allyGo != null) { var ally = allyGo.GetComponent(); if (ally != null) { int added = ally.AddScoreForJudge("Miss"); ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added); } } } catch (System.Exception ex) { Debug.LogWarning($"[Note] Failed to add per-track score for Miss: {ex}"); } // Notify SkillBuilder about Miss so OnNoteHit skills configured for Miss can trigger try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, "Miss", SkillDefinition.NoteTypeTrigger.Tap); } catch { } // InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); // 已在HandlePress中调用 for manual presses ReturnToPool(); } private void ReturnToPool() { InputManager.OnKeyPressed -= HandlePress; if (controller != null) { controller.ResetState(); } gameObject.SetActive(false); NotePool.Instance.ReturnNote(gameObject, noteColor); } public int GetTrackIndex() { return TrackIndex; } public KeyCode GetKey() { return keyToPress; } /// /// 由 Controller 通知 Note 是否进入判定区 /// public void SetJudgeZone(bool inZone) { // 离开判定区且未判定则判 Miss if (!inZone && !isJudged) { // Show Miss and update combo InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); JudgeSoundManager.Instance?.PlayJudgeSound("Miss"); teamUIController.Instance?.OnJudgeResult("Miss"); JudgeMiss(); } } }