hold音符补丁 备份

This commit is contained in:
2026-03-03 14:07:39 +08:00
parent b8704629a3
commit 9011fa022e
36 changed files with 2721 additions and 256 deletions
+77 -17
View File
@@ -60,6 +60,11 @@ public class HoldNote : BaseNote
[Range(1f, 2f)]
public float holdWindowMultiplier = 1.3f;
[Header("Custom Animation")]
public GameObject holdAnimationPrefab;
private GameObject activeHoldAnimationInstance;
private Coroutine delayedHoldAnimationCoroutine;
public float visualSpeedMultiplier = 1f; // injected from NoteSpawner to adapt windows when visual speed changes
// track when the hold actually started (real-time) so we can compute held fraction
@@ -317,6 +322,58 @@ public class HoldNote : BaseNote
}
}
private void StartHoldEffects()
{
StopHoldEffects(); // cleanup any existing
if (gameObject.activeInHierarchy)
{
delayedHoldAnimationCoroutine = StartCoroutine(DelayedStartHoldEffects());
}
}
private void StopHoldEffects()
{
if (delayedHoldAnimationCoroutine != null)
{
StopCoroutine(delayedHoldAnimationCoroutine);
delayedHoldAnimationCoroutine = null;
}
if (activeHoldAnimationInstance != null)
{
Destroy(activeHoldAnimationInstance);
activeHoldAnimationInstance = null;
}
AnimationController.Global?.StopHoldParticles();
}
private IEnumerator DelayedStartHoldEffects()
{
yield return new WaitForSeconds(0.1f);
// Start standard particles
AnimationController.Global?.StartHoldParticles(noteColor);
// Instantiate custom prefab if assigned
if (holdAnimationPrefab != null)
{
// find spawn position: prefer the track effect position if available from AnimationController
Vector3 spawnPos = transform.position;
var ac = AnimationController.Global;
if (ac != null)
{
// Logic to find slot position (simplified: use ac's position or the note's)
// In AnimationController.PlayDestroyAnimation, it uses redEffect.transform.position etc.
// We don't have direct access to those private fields, but we can assume
// the note is at the judge line when this starts.
}
activeHoldAnimationInstance = Instantiate(holdAnimationPrefab, spawnPos, Quaternion.identity);
// Optionally parent to the note or track
// activeHoldAnimationInstance.transform.SetParent(this.transform, false);
}
}
private IEnumerator DelayedDisableAnimation(GameObject animationObject, float delay)
{
// No-op: we used to disable the shared AnimationController here which stopped coroutines.
@@ -440,7 +497,7 @@ public class HoldNote : BaseNote
if (!isHoldActive && jm != null && jm.IsStartJudged(noteID) && !jm.HasNoteReleased(noteID) && keyHeld)
{
isHoldActive = true;
AnimationController.Global?.StartHoldParticles(noteColor);
StartHoldEffects();
if (debugEnabled) Debug.Log($"[HoldNote] Start particles started for {noteID} color={noteColor} at time={now:F3}");
}
@@ -474,7 +531,7 @@ public class HoldNote : BaseNote
if (isHoldActive && keyUp)
{
isHoldActive = false;
AnimationController.Global?.StopHoldParticles();
StopHoldEffects();
if (debugEnabled) Debug.Log($"[HoldNote] KeyUp {keyToPress} detected. NoteID: {noteID}, Segment: {segment}, Type: {type}");
if (!hasReleased)
@@ -649,7 +706,7 @@ public class HoldNote : BaseNote
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
StartHoldEffects();
holdStartTime = pressTime;
HoldNoteJudgePool.RegisterStart(noteID, pressTime, hitTime, scheduledEndTime, noteColor, trackIndex, (object)noteData);
@@ -695,17 +752,17 @@ public class HoldNote : BaseNote
// Autoplay must ignore physical input to guarantee Perfect at scheduledEndTime.
if (!GameConfig.autoPlayEnabled && Input.GetKey(keyToPress) && JudgeManager.Instance.IsStartJudged(noteID) && !isJudged)
{
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] End entered while key still held: {noteColor}, calculating result immediately");
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] End entered while key still held: {noteColor}, forcing Perfect");
// Record release time as current time (player is still holding)
releaseTime = Time.time;
hasReleased = true;
JudgeManager.Instance?.RegisterNoteReleased(noteID, true);
// Evaluate and judge the hold end
EvaluateHoldEnd(releaseTime, false);
// Evaluate and judge the hold end - force Perfect since we reached the end while holding
EvaluateHoldEnd(releaseTime, false, true);
isJudged = true;
// Terminate input - mark key as released
isHoldActive = false;
AnimationController.Global?.StopHoldParticles();
StopHoldEffects();
return;
}
@@ -875,7 +932,7 @@ public class HoldNote : BaseNote
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
StartHoldEffects();
// record when the hold started so we can compute held fraction later
holdStartTime = pressTime;
@@ -891,7 +948,7 @@ public class HoldNote : BaseNote
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
StartHoldEffects();
holdStartTime = pressTime;
@@ -905,7 +962,7 @@ public class HoldNote : BaseNote
JudgeManager.Instance.RegisterStartJudged(noteID, true);
isHoldActive = true;
PlayHitAnimation();
AnimationController.Global?.StartHoldParticles(noteColor);
StartHoldEffects();
holdStartTime = pressTime;
@@ -986,7 +1043,7 @@ public class HoldNote : BaseNote
}
// central evaluation for hold end, given an actual releaseTime (real-time) or forceMiss
private void EvaluateHoldEnd(float actualReleaseTime, bool forceMiss)
private void EvaluateHoldEnd(float actualReleaseTime, bool forceMiss, bool forcePerfect = false)
{
if (isJudged) return; // already evaluated
@@ -1001,7 +1058,7 @@ public class HoldNote : BaseNote
hasReleased = true;
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] EvaluateHoldEnd: noteID={noteID} releaseTime={releaseTime:F3} scheduledEnd={scheduledEndTime:F3} forceMiss={forceMiss}");
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] EvaluateHoldEnd: noteID={noteID} releaseTime={releaseTime:F3} scheduledEnd={scheduledEndTime:F3} forceMiss={forceMiss} forcePerfect={forcePerfect}");
string result;
@@ -1028,13 +1085,13 @@ public class HoldNote : BaseNote
float playerHeld = Mathf.Clamp(releaseTime - info.pressTime, 0f, info.length);
float frac = info.length <= 0f ? 0f : (playerHeld / info.length);
if (frac > 0.8f)
if (forcePerfect || frac > 0.7f)
{
result = "Perfect";
ScoreManager.Instance.countPerfect += 1;
if (trackIndex >= 0 && trackIndex < 5) ScoreManager.Instance.trackPerfectCounts[trackIndex]++;
}
else if (frac > 0.5f)
else if (frac > 0.4f)
{
result = "Great";
ScoreManager.Instance.countGreat += 1;
@@ -1069,13 +1126,13 @@ public class HoldNote : BaseNote
float held = Mathf.Clamp(actualReleaseTime - hitTime, 0f, holdRequired);
float frac = holdRequired <= 0f ? 0f : (held / holdRequired);
if (frac > 0.8f)
if (forcePerfect || frac > 0.7f)
{
result = "Perfect";
ScoreManager.Instance.countPerfect += 1;
if (trackIndex >= 0 && trackIndex < 5) ScoreManager.Instance.trackPerfectCounts[trackIndex]++;
}
else if (frac > 0.5f)
else if (frac > 0.4f)
{
result = "Great";
ScoreManager.Instance.countGreat += 1;
@@ -1193,7 +1250,7 @@ public class HoldNote : BaseNote
if (JudgeManager.IsDebugEnabled) Debug.Log($"[HoldNote] Hold end NotifyNoteHit called: TrackIndex={TrackIndex} trackIndexField={trackIndex} result={result} triggeredHold={triggeredHold} triggeredTap={triggeredTap}");
}
AnimationController.Global?.StopHoldParticles();
StopHoldEffects();
// Release the track lock immediately after end evaluation so the next hold can start on time
ReleaseTrackJudgeLockSafe();
@@ -1315,6 +1372,9 @@ public class HoldNote : BaseNote
// Restore materials before returning to pool/reusing
RestoreOriginalMaterials();
// Stop any active hold animations or delayed starts
StopHoldEffects();
hasReleased = false;
segment = NoteSegment.None;
keyToPress = KeyCode.None;