修复一堆bug 加入粒子系统代替原击打特效 焕新长音符逻辑 修复多重计分 更新判定管理和音符生成器 搞了一个免费包

This commit is contained in:
FloatGaming
2025-12-18 03:26:52 +08:00
parent 1dd6c95ec9
commit a7d8d71d10
1572 changed files with 1658779 additions and 527 deletions
+250 -119
View File
@@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
@@ -32,21 +32,37 @@ public class HoldNote : BaseNote
public KeyCode key;
public string type; // "start", "middle", or "end"
private bool isJudged = false; // 分段判定标记,一旦判定(无论成功失败)就设为true,避免重复判定
private bool isHoldActive = false; // 长按有效标记:Start段成功判定且按键未松开
private bool isJudged = false; // 分段判定标记,一旦判定(无论成功失败)就设为true,避免重复判定
private bool isHoldActive = false; // 长按有效标记:Start段成功判定且按键未松开
// 新增变量
private bool hasBeenHeldFromStart = false; // 进入判定区时是否已按住 (用于辅助判断,但不再直接阻断判定)
// 新增变量
private bool hasBeenHeldFromStart = false; // 进入判定区时是否已按住 (用于辅助判断,但不再直接阻断判定)
[Header("判定区间配置")]
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
[Header("判定区间配置")]
public NoteJudgeConfig judgeConfig; // 判定区间配置,需在预制体或生成时赋值
private NoteData noteData;
private bool _hasTriggeredOnThisHold = false; // ensure single trigger per hold note
// private bool _hasTriggeredOnThisHold = false; // ensure single trigger per hold note
private void Awake()
{
controller = GetComponent<HoldNoteController>();
anim = GetComponent<AnimationController>();
// Prefer the global shared AnimationController if available, otherwise fallback to local or scene instance
anim = AnimationController.Global;
if (anim == null)
{
anim = GetComponent<AnimationController>();
if (anim == null)
{
anim = FindObjectOfType<AnimationController>();
}
}
if (anim == null)
{
Debug.LogWarning("HoldNote: No AnimationController found (Global/local/scene). Particle effects will be unavailable.");
}
if (controller != null)
{
controller.OnJudgeZoneChanged += OnJudgeZoneChanged;
@@ -73,30 +89,26 @@ public class HoldNote : BaseNote
private void PlayHitAnimation()
{
if (anim != null)
var controller = AnimationController.Global ?? anim;
if (controller != null)
{
// 激活动画效果 GameObject
anim.gameObject.SetActive(true);
anim.PlayDestroyAnimation(noteColor);
// 延迟禁用动画效果 GameObject,确保动画播放完毕
StartCoroutine(DelayedDisableAnimation(anim.gameObject, 0.5f)); // 0.5秒后禁用
controller.PlayDestroyAnimation(noteColor);
}
}
private IEnumerator DelayedDisableAnimation(GameObject animationObject, float delay)
{
yield return new WaitForSeconds(delay);
animationObject.SetActive(false);
// No-op: we used to disable the shared AnimationController here which stopped coroutines.
// Keep as no-op to avoid side effects.
yield return null;
}
public void Setup(int id, int trackIndex, float speed, float time, float delay,
bool isEnd, float scheduledEnd, string color, KeyCode key, string type, NoteJudgeConfig judgeConfig)
bool isEnd, float scheduledEnd, string color, KeyCode key, string type, NoteJudgeConfig judgeConfig, NoteData noteData)
{
this.id = id;
this.trackIndex = trackIndex;
// also set BaseNote.TrackIndex so other systems using TrackIndex property work consistently
this.TrackIndex = trackIndex;
this.speed = speed;
this.startTime = time;
this.delay = delay;
@@ -117,19 +129,21 @@ public class HoldNote : BaseNote
this.hasEnteredLine = false;
this.hasReleased = false;
// 判定区间配置检查
// 判定区间配置检查
if (judgeConfig == null)
Debug.LogError($"HoldNoteJudgeConfig is null! 判定区间配置未传入!track={trackIndex}");
Debug.LogError($"HoldNoteJudgeConfig is null! 判定区间配置未传入!track={trackIndex}");
else
Debug.Log($"HoldNoteJudgeConfig: perfect={judgeConfig.perfectRange}, great={judgeConfig.greatRange}, good={judgeConfig.goodRange}, miss={judgeConfig.missRange}");
this.noteData = noteData;
// Debug info to help investigate end note visibility
Debug.Log($"[HoldNote.Setup] id={noteID} segment={segment} type={type} hitTime={hitTime:F2} scheduledEnd={scheduledEndTime:F2} color={color} track={trackIndex}");
// 在 Setup 时注册初始状态
// 在 Setup 时注册初始状态
JudgeManager.Instance?.RegisterNoteReleased(noteID, false);
if (segment == NoteSegment.Start)
JudgeManager.Instance?.RegisterStartJudged(noteID, false); // 初始为未判定
JudgeManager.Instance?.RegisterStartJudged(noteID, false); // 初始为未判定
if (segment == NoteSegment.End)
JudgeManager.Instance?.RegisterScheduledEndTime(noteID, scheduledEndTime);
@@ -140,33 +154,50 @@ public class HoldNote : BaseNote
private void Update()
{
// 如果当前片段已经判定过,直接返回
// 如果当前片段已经判定过,直接返回
if (isJudged) return;
// 自动 Miss 判定:当音符经过判定线且未被判定时
// 重新启动长按粒子协程,如果长按状态恢复且协程未运行
if (!isHoldActive && JudgeManager.Instance.IsStartJudged(noteID) && !JudgeManager.Instance.HasNoteReleased(noteID) && Input.GetKey(keyToPress))
{
isHoldActive = true;
AnimationController.Global?.StartHoldParticles(noteColor);
}
// 自动 Miss 判定:当音符经过判定线且未被判定时
if (hasEnteredLine && !isJudged)
{
if (segment == NoteSegment.Start && Time.time > hitTime + judgeConfig?.missRange)
if(segment == NoteSegment.Start && Time.time > hitTime + judgeConfig?.missRange)
{
Debug.Log($"[HoldNote] START段超时自动Miss: {noteColor}");
HandleStart();
Debug.Log($"[HoldNote] START段超时自动Miss(自动): {noteColor}");
// 只做 Miss 结果展示与登记
JudgeManager.Instance.RegisterStartJudged(noteID, false);
InputManager.Instance?.ShowJudgeResult(trackIndex, "Miss");
JudgeSoundManager.Instance?.PlayJudgeSound("Miss");
teamUIController.Instance?.OnJudgeResult("Miss");
isJudged = true;
ReturnToPool();
}
else if (segment == NoteSegment.End && Time.time > scheduledEndTime + (judgeConfig?.missRange ?? 0.3f))
{
Debug.Log($"[HoldNote] END段超时自动Miss: {noteColor}");
Debug.Log($"[HoldNote] END段超时自动Miss: {noteColor}");
HandleEnd(true);
isJudged = true;
}
}
// **通用长按状态更新:**
// **通用长按状态更新:**
if (isHoldActive && Input.GetKeyUp(keyToPress))
{
isHoldActive = false;
Debug.Log($"[HoldNote] 按键 {keyToPress} 松开,长按状态失效。NoteID: {noteID}, Segment: {segment}, Type: {type}");
AnimationController.Global?.StopHoldParticles();
Debug.Log($"[HoldNote] 按键 {keyToPress} 松开,长按状态失效。NoteID: {noteID}, Segment: {segment}, Type: {type}");
// 当玩家松开按键时,向 JudgeManager 注册该长音符已被释放,确保尾部能正确检查释放状态
// 当玩家松开按键时,向 JudgeManager 注册该长音符已被释放,确保尾部能正确检查释放状态
if (!hasReleased)
{
hasReleased = true;
@@ -176,21 +207,21 @@ public class HoldNote : BaseNote
}
}
if (JudgeManager.Instance.IsStartJudged(noteID) && Input.GetKey(keyToPress))
/*if (JudgeManager.Instance.IsStartJudged(noteID) && Input.GetKey(keyToPress))
{
isHoldActive = true;
}
}*/
// Start段判定
// Start段判定
if (segment == NoteSegment.Start && hasEnteredLine)
{
if (Input.GetKeyDown(keyToPress))
{
HandleStart();
isJudged = true; // 无论成功失败都标记为已判定,避免重复尝试
isJudged = true; // 无论成功失败都标记为已判定,避免重复尝试
}
}
// Middle段判定:中段只负责视觉/回收,不做 End 判定统计
// Middle段判定:中段只负责视觉/回收,不做 End 判定统计
else if (segment == NoteSegment.Middle
&& JudgeManager.Instance.IsStartJudged(noteID)
&& isHoldActive
@@ -200,16 +231,16 @@ public class HoldNote : BaseNote
{
if (hasEnteredLine)
{
Debug.Log($"[HoldNote] Middle段通过 (持续长按): {noteColor}");
Debug.Log($"[HoldNote] Middle段通过 (持续长按): {noteColor}");
PlayHitAnimation();
// 把通过事件上报给 JudgeManager(记录中段通过)
// 把通过事件上报给 JudgeManager(记录中段通过)
JudgeManager.Instance?.RegisterMiddlePassed(noteID);
ReturnToPool();
isJudged = true;
}
}
}
// End段判定:现在 End 段判定由独立的尾部音符负责(即本对象仍可判定,但更重要的是尾部note会决定最终判定)
// End段判定:现在 End 段判定由独立的尾部音符负责(即本对象仍可判定,但更重要的是尾部note会决定最终判定)
else if (segment == NoteSegment.End
&& JudgeManager.Instance.IsStartJudged(noteID)
&& !JudgeManager.Instance.HasNoteReleased(noteID))
@@ -231,7 +262,7 @@ public class HoldNote : BaseNote
if (segment == NoteSegment.Middle)
{
hasBeenHeldFromStart = Input.GetKey(keyToPress);
Debug.Log($"[HoldNote] Middle段进入判定线: color={noteColor}, isStartJudged={JudgeManager.Instance.IsStartJudged(noteID)}, hasReleased={JudgeManager.Instance.HasNoteReleased(noteID)}, isKeyHeld={Input.GetKey(keyToPress)}, hasBeenHeldFromStart={hasBeenHeldFromStart}");
Debug.Log($"[HoldNote] Middle段进入判定线: color={noteColor}, isStartJudged={JudgeManager.Instance.IsStartJudged(noteID)}, hasReleased={JudgeManager.Instance.HasNoteReleased(noteID)}, isKeyHeld={Input.GetKey(keyToPress)}, hasBeenHeldFromStart={hasBeenHeldFromStart}");
if (autoReturnCoroutine != null)
StopCoroutine(autoReturnCoroutine);
@@ -241,10 +272,10 @@ public class HoldNote : BaseNote
if (autoReturnCoroutine != null)
StopCoroutine(autoReturnCoroutine);
// 如果在尾部进入判定区时玩家已经释放按键,则此时应立即做 End 判定
// 如果在尾部进入判定区时玩家已经释放按键,则此时应立即做 End 判定
if (JudgeManager.Instance.HasNoteReleased(noteID) && !isJudged)
{
Debug.Log($"[HoldNote] End段进入判定线但已经释放,立即执行 End 判定: {noteColor}");
Debug.Log($"[HoldNote] End段进入判定线但已经释放,立即执行 End 判定: {noteColor}");
HandleEnd();
isJudged = true;
// ReturnToPool handled inside HandleEnd
@@ -263,8 +294,8 @@ public class HoldNote : BaseNote
{
if (!JudgeManager.Instance.IsStartJudged(noteID))
{
Debug.Log($"[HoldNote] Start段离开判定线未判定,补偿 Miss 并回收: {noteColor}");
// 确保未判定的 Start 段在离开判定线时登记为 Miss
Debug.Log($"[HoldNote] Start段离开判定线未判定,补偿 Miss 并回收: {noteColor}");
// 确保未判定的 Start 段在离开判定线时登记为 Miss
HandleStart();
isJudged = true;
ReturnToPool();
@@ -272,7 +303,7 @@ public class HoldNote : BaseNote
}
else if (segment == NoteSegment.Middle)
{
Debug.Log($"[HoldNote] Middle段离开判定线强制回收: {noteColor}");
Debug.Log($"[HoldNote] Middle段离开判定线强制回收: {noteColor}");
ReturnToPool();
}
else if (segment == NoteSegment.End)
@@ -283,15 +314,15 @@ public class HoldNote : BaseNote
autoReturnCoroutine = null;
}
// 在尾部离开判定线时,按如下规则处理:
// - 如果头部未判定:补偿 Miss 并回收
// - 如果头部已判定且玩家已经释放:执行 End 判定并回收
// - 如果头部已判定且玩家仍在按住:不要强制 Miss,也不要立即回收,等待玩家松手或超时处理
// 在尾部离开判定线时,按如下规则处理:
// - 如果头部未判定:补偿 Miss 并回收
// - 如果头部已判定且玩家已经释放:执行 End 判定并回收
// - 如果头部已判定且玩家仍在按住:不要强制 Miss,也不要立即回收,等待玩家松手或超时处理
if (!isJudged)
{
if (!JudgeManager.Instance.IsStartJudged(noteID))
{
Debug.Log($"[HoldNote] End段离开判定线且头部未判定,补偿 Miss: {noteColor}");
Debug.Log($"[HoldNote] End段离开判定线且头部未判定,补偿 Miss: {noteColor}");
if (!JudgeManager.Instance.HasNoteReleased(noteID))
HandleEnd(true);
ReturnToPool();
@@ -300,15 +331,15 @@ public class HoldNote : BaseNote
{
if (JudgeManager.Instance.HasNoteReleased(noteID))
{
Debug.Log($"[HoldNote] End段离开判定线且已释放,执行 End 判定: {noteColor}");
Debug.Log($"[HoldNote] End段离开判定线且已释放,执行 End 判定: {noteColor}");
HandleEnd();
isJudged = true;
ReturnToPool();
}
else
{
// 头部已判定且玩家仍在按住:保持显示,不作回收或判定
Debug.Log($"[HoldNote] End段离开判定线,头部已判定且仍在按住,保持显示等待松手或超时: {noteColor}");
// 头部已判定且玩家仍在按住:保持显示,不作回收或判定
Debug.Log($"[HoldNote] End段离开判定线,头部已判定且仍在按住,保持显示等待松手或超时: {noteColor}");
}
}
}
@@ -341,19 +372,28 @@ public class HoldNote : BaseNote
private void HandleStart()
{
if (!JudgeManager.Instance.TryResolveStart(noteID))
{
Debug.Log($"[HoldNote] START 已被判定,跳过: {noteColor}");
return;
}
float pressTime = Time.time;
float rawOffsetMs = (hitTime - pressTime) * 1000f;
float offset = Mathf.Abs(pressTime - hitTime);
string result;
if (offset < 0.1f) // 判定成功
if (offset < (judgeConfig?.perfectRange ?? 0.1f)) // 判定成功
{
result = "Perfect";
Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
ScoreManager.Instance.countPerfect += 1;
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true; // 成功判定Start,长按状态激活
isHoldActive = true; // 成功判定Start,长按状态激活
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
// Trigger skills configured to fire on note hits for this slot when the Start is successfully hit.
if (!_hasTriggeredOnThisHold)
if (/*!_hasTriggeredOnThisHold*/JudgeManager.Instance.TryTriggerSkill(noteID))
{
try
{
@@ -371,15 +411,21 @@ public class HoldNote : BaseNote
{
Debug.LogError($"[HoldNote] NotifyNoteHit(Start) threw: {ex}");
}
_hasTriggeredOnThisHold = true; // avoid triggering again at End
// _hasTriggeredOnThisHold = true; // avoid triggering again at End
}
}
else
{
result = "Miss";
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, false); // 明确标记未判定
isHoldActive = false; // Start Miss,长按状态不激活
if (JudgeManager.Instance.TryResolveStart(noteID)) // 或 TryResolveEnd
{
ScoreManager.Instance.countMiss += 1;
}
// if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, false); // 明确标记未判定
isHoldActive = false; // Start Miss,长按状态不激活
// For Miss we should still notify shared logic once so it behaves like short notes
try
@@ -392,15 +438,39 @@ public class HoldNote : BaseNote
{
Debug.LogError($"[HoldNote] NotifyNoteHit(Start Miss) threw: {ex}");
}
_hasTriggeredOnThisHold = true; // ensure we don't trigger again at End
// _hasTriggeredOnThisHold = true; // ensure we don't trigger again at End
}
// show judge result and play sound / update combo like short notes
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
JudgeSoundManager.Instance?.PlayJudgeSound(result);
teamUIController.Instance?.OnJudgeResult(result);
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result);
StartCoroutine(DelayedReturn());
// --- Add scoring for Start like short notes ---
try
{
var allyGo = GameObject.Find($"ally_0{TrackIndex + 1}");
if (allyGo != null)
{
var ally = allyGo.GetComponent<AllyCombatant>();
if (ally != null)
{
int added = ally.AddScoreForJudge(result);
float efficiency = ally.scoreEfficiency;
ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added, efficiency);
}
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[HoldNote] Failed to add per-track score for START: {ex}");
}
if (segment != NoteSegment.Start)
{
StartCoroutine(DelayedReturn());
}
}
private IEnumerator DelayedReturn()
@@ -414,7 +484,12 @@ public class HoldNote : BaseNote
private void HandleEnd(bool forceMiss = false)
{
// 记录释放时间并在 JudgeManager 中登记已释放状态
if (!JudgeManager.Instance.TryResolveEnd(noteID))
{
Debug.Log($"[HoldNote] END 已被判定,跳过: {noteColor}");
return;
}
// 记录释放时间并在 JudgeManager 中登记已释放状态
if (!hasReleased)
{
releaseTime = Time.time;
@@ -423,15 +498,17 @@ public class HoldNote : BaseNote
}
string result;
float rawOffsetMsEnd = 0f;
// 如果玩家在尾部进入判定区前就松手,则仅登记释放,不在此处进行最终判定和回收,除非是强制 Miss
// 如果玩家在尾部进入判定区前就松手,则仅登记释放,不在此处进行最终判定和回收,除非是强制 Miss
if (!hasEnteredLine && !forceMiss)
{
Debug.Log($"[HoldNote] HandleEnd: 提前松手,记录释放但等待尾部进入判定区再判定. NoteID={noteID}, color={noteColor}");
Debug.Log($"[HoldNote] HandleEnd: 提前松手,记录释放但等待尾部进入判定区再判定. NoteID={noteID}, color={noteColor}");
return;
}
// 现在进行正常判定逻辑
// 现在进行正常判定逻辑
releaseTime = Time.time;
hasReleased = true;
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
@@ -439,56 +516,101 @@ public class HoldNote : BaseNote
if (forceMiss || !JudgeManager.Instance.IsStartJudged(noteID))
{
result = "Miss";
Debug.LogWarning($"[HoldNote] END判定失败({noteColor}):{(forceMiss ? "Miss" : "")}");
}
else if (judgeConfig != null)
{
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
if (diff <= judgeConfig.perfectRange)
// if (JudgeManager.Instance.TryResolveEnd(noteID))
{
result = "Perfect";
}
else if (diff <= judgeConfig.greatRange)
{
result = "Great";
}
else if (diff <= judgeConfig.goodRange)
{
result = "Good";
}
else if (diff <= judgeConfig.missRange)
{
result = "Miss";
}
else
{
result = "Miss";
if (result == "Perfect") ScoreManager.Instance.countPerfect += 1;
else if (result == "Great") ScoreManager.Instance.countGreat += 1;
else if (result == "Good") ScoreManager.Instance.countGood += 1;
else ScoreManager.Instance.countMiss += 1;
}
Debug.LogWarning($"[HoldNote] END判定失败({noteColor}):{(forceMiss ? "Miss" : "")}");
}
else
{
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
if (diff <= 0.1f)
rawOffsetMsEnd = (scheduledEndTime - releaseTime) * 1000f;
if (judgeConfig != null)
{
result = "Perfect";
}
else if (diff < 0.2f)
{
result = "Great";
}
else if (diff < 0.3f)
{
result = "Good";
if (diff <= judgeConfig.perfectRange)
{
result = "Perfect";
}
else if (diff <= judgeConfig.greatRange)
{
result = "Great";
}
else if (diff <= judgeConfig.goodRange)
{
result = "Good";
}
else if (diff <= judgeConfig.missRange)
{
result = "Miss";
}
else
{
result = "Miss";
}
}
else
{
result = "Bad";
if (diff <= 0.1f)
{
result = "Perfect";
}
else if (diff < 0.2f)
{
result = "Great";
}
else if (diff < 0.3f)
{
result = "Good";
}
else
{
result = "Bad";
}
}
// --- 新增:根据最终确定的 result 统计 ---
if (result == "Perfect") ScoreManager.Instance.countPerfect += 1;
else if (result == "Great") ScoreManager.Instance.countGreat += 1;
else if (result == "Good") ScoreManager.Instance.countGood += 1;
else ScoreManager.Instance.countMiss += 1;
// --- 新增:记录偏差数据到 NoteData ---
if (noteData != null && result != "Miss")
{
noteData.judgeOffsetMsEnd = rawOffsetMsEnd;
}
}
InputManager.Instance?.ShowJudgeResult(trackIndex, result);
teamUIController.Instance?.OnJudgeResult(result); // 新增:更新combo计数
teamUIController.Instance?.OnJudgeResult(result); // 新增:更新combo计数
Animation_GenerateJudgementSituationPrefab.Instance?.SpawnJudgePrefab(noteColor, result);
// --- Add scoring for End like short notes ---
try
{
var allyGo = GameObject.Find($"ally_0{TrackIndex + 1}");
if (allyGo != null)
{
var ally = allyGo.GetComponent<AllyCombatant>();
if (ally != null)
{
int added = ally.AddScoreForJudge(result);
float efficiency = ally.scoreEfficiency;
ScoreManager.Instance?.AddPmScoreForTrack(TrackIndex, added, efficiency);
}
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[HoldNote] Failed to add per-track score for END: {ex}");
}
// Notify SkillBuilder so Hold notes can trigger skills configured for Hold
if (!_hasTriggeredOnThisHold)
if (JudgeManager.Instance.TryTriggerSkill(noteID))
{
var sb = SkillBuilder.Instance;
if (sb == null)
@@ -499,7 +621,7 @@ public class HoldNote : BaseNote
{
var so = sb.GetAllyHeroSOBySlot(TrackIndex);
var def = so?.GetPrimarySkill();
Debug.Log($"[HoldNote] About to NotifyNoteHit: TrackIndex={TrackIndex} trackIndexField={trackIndex} so={(so!=null?so.name:"null")} skill={(def!=null?def.skillId:"null")} trigger={(def!=null?def.triggerCondition.ToString():"-")}");
Debug.Log($"[HoldNote] About to NotifyNoteHit: TrackIndex={TrackIndex} trackIndexField={trackIndex} so={(so != null ? so.name : "null")} skill={(def != null ? def.skillId : "null")} trigger={(def != null ? def.triggerCondition.ToString() : "-")}");
}
bool triggeredHold = false;
@@ -566,15 +688,20 @@ public class HoldNote : BaseNote
}
}
_hasTriggeredOnThisHold = true;
// _hasTriggeredOnThisHold = true;
Debug.Log($"[HoldNote] Hold end NotifyNoteHit called: TrackIndex={TrackIndex} trackIndexField={trackIndex} result={result} triggeredHold={triggeredHold} triggeredTap={triggeredTap}");
}
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
AnimationController.Global?.StopHoldParticles();
ReturnToPool();
}
private void OnDisable()
{
if (segment == NoteSegment.End)
{
AnimationController.Global?.StopHoldParticles();
}
if (autoReturnCoroutine != null)
{
StopCoroutine(autoReturnCoroutine);
@@ -582,19 +709,23 @@ public class HoldNote : BaseNote
}
if (segment == NoteSegment.End &&
!isJudged && // 尚未判定
JudgeManager.Instance.IsStartJudged(noteID)) // 头部已判定
!isJudged && // 尚未判定
JudgeManager.Instance.IsStartJudged(noteID)) // 头部已判定
{
// 只在头部已判定且玩家已松手时补偿 End 判定为 Miss;否则不要强制 Miss
// 只在头部已判定且玩家已松手时补偿 End 判定为 Miss;否则不要强制 Miss
if (JudgeManager.Instance.HasNoteReleased(noteID) && !isHoldActive)
{
Debug.Log($"[HoldNote] OnDisable补偿End段判定(已释放): {noteColor}");
HandleEnd(); // 正常判定(基于 releaseTime
Debug.Log($"[HoldNote] OnDisable补偿End段判定(已释放): {noteColor}");
if (JudgeManager.Instance.TryResolveEnd(noteID))
{
HandleEnd();
}
isJudged = true;
}
else
{
Debug.Log($"[HoldNote] OnDisableEnd段未判定且尚有按键状态,跳过强制 Miss: {noteColor}");
Debug.Log($"[HoldNote] OnDisableEnd段未判定且尚有按键状态,跳过强制 Miss: {noteColor}");
}
}
@@ -603,11 +734,11 @@ public class HoldNote : BaseNote
private void ReturnToPool()
{
if (!gameObject.activeSelf) return; // 再次检查以防止对象已被禁用
if (!gameObject.activeSelf) return; // 再次检查以防止对象已被禁用
if (segment == NoteSegment.Start && !hasEnteredLine)
{
Debug.LogWarning($"[HoldNote] 阻止回收尚未进入判定线的 Start段: {noteColor}");
Debug.LogWarning($"[HoldNote] 阻止回收尚未进入判定线的 Start段: {noteColor}");
return;
}
@@ -632,9 +763,9 @@ public class HoldNote : BaseNote
scheduledEndTime = 0f;
hasEnteredLine = false;
isJudged = false;
isHoldActive = false; // 重置长按标记
hasBeenHeldFromStart = false; // 重置
_hasTriggeredOnThisHold = false; // 重置
isHoldActive = false; // 重置长按标记
hasBeenHeldFromStart = false; // 重置
// _hasTriggeredOnThisHold = false; // 重置
if (autoReturnCoroutine != null)
{