using UnityEngine; public class Note : BaseNote { private KeyCode keyToPress; private string noteColor; private AnimationController anim; private NoteController controller; private bool isJudged = false; private NoteData noteData; [Header("�ж���������")] public NoteJudgeConfig judgeConfig; // �ж��������ã�����Ԥ���������ʱ��ֵ // Cache allies per track to avoid GameObject.Find on every judge. private static AllyCombatant[] allyCache; private static AllyCombatant GetAllyForTrackCached(int trackIndex) { if (trackIndex < 0) return null; if (allyCache == null || allyCache.Length < 10) allyCache = new AllyCombatant[10]; if (allyCache[trackIndex] != null) return allyCache[trackIndex]; var allyGo = GameObject.Find($"ally_0{trackIndex + 1}"); if (allyGo != null) { allyCache[trackIndex] = allyGo.GetComponent(); } return allyCache[trackIndex]; } private void Awake() { anim = GetComponent(); controller = GetComponent(); } public void Setup(KeyCode key, int trackIndex, float speed, float hitTime, string color, NoteJudgeConfig judgeConfig, NoteData data) { keyToPress = key; noteColor = color; TrackIndex = trackIndex; Speed = speed; this.hitTime = hitTime; isJudged = false; this.judgeConfig = judgeConfig; this.noteData = data; if (controller != null) { controller.SetSpeed(speed); } InputManager.OnKeyPressed += HandlePress; // �ж��������ü�� if (judgeConfig == null) { if (GameConfig.verboseLogs) Debug.LogError($"NoteJudgeConfig is null! �ж���������δ���룡track={trackIndex}"); } else { if (GameConfig.verboseLogs) 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); // ��������ƫ���ֵ��ǰ����ֵ��� float rawOffsetMs = (hitTime - Time.time) * 1000f; string judgeResult = null; if (judgeConfig != null) { if (timeDifference <= judgeConfig.perfectRange) { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Perfect"); judgeResult = "Perfect"; ScoreManager.Instance.countPerfect += 1; if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs; } else if (timeDifference <= judgeConfig.greatRange) { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Great"); judgeResult = "Great"; ScoreManager.Instance.countGreat += 1; if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs; } else if (timeDifference <= judgeConfig.goodRange) { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Good"); judgeResult = "Good"; ScoreManager.Instance.countGood += 1; if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs; } else { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Miss"); judgeResult = "Miss"; } } else { // fallback timing if (timeDifference <= 0.08f) { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Perfect"); judgeResult = "Perfect"; } else if (timeDifference <= 0.15f) { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Great"); judgeResult = "Great"; } else { if (GameConfig.verboseLogs) Debug.Log($"{keyToPress}: Miss"); judgeResult = "Miss"; } } if (!string.IsNullOrEmpty(judgeResult)) { InputManager.Instance?.ShowJudgeResult(TrackIndex, judgeResult); JudgeSoundManager.Instance?.PlayJudgeSound(judgeResult); teamUIController.Instance?.OnJudgeResult(judgeResult); Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, judgeResult); try { var ally = GetAllyForTrackCached(TrackIndex); if (ally != null) { int added = ally.AddScoreForJudge(judgeResult); float efficiency = ally.scoreEfficiency; ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added, efficiency); } } catch (System.Exception ex) { Debug.LogWarning($"[Note] Failed to add per-track score: {ex}"); } 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() { // idempotent: this can be called from multiple paths if (isJudged) return; isJudged = true; ScoreManager.Instance.countMiss += 1; if (GameConfig.verboseLogs) Debug.Log($"{keyToPress} Miss"); InputManager.Instance?.ShowJudgeResult(TrackIndex, "Miss"); JudgeSoundManager.Instance?.PlayJudgeSound("Miss"); teamUIController.Instance?.OnJudgeResult("Miss"); Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, "Miss"); try { var ally = GetAllyForTrackCached(TrackIndex); if (ally != null) { int added = ally.AddScoreForJudge("Miss"); float efficiency = ally.scoreEfficiency; ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added, efficiency); } } catch (System.Exception ex) { Debug.LogWarning($"[Note] Failed to add per-track score for Miss: {ex}"); } try { SkillBuilder.Instance?.NotifyNoteHit(TrackIndex, "Miss", SkillDefinition.NoteTypeTrigger.Tap); } catch { } 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) { // leaving judge zone without being judged -> Miss if (!inZone) { // JudgeMiss already handles UI/sound and prevents double execution. JudgeMiss(); } } }