备份,准备做双端

This commit is contained in:
2026-07-30 23:15:58 +08:00
parent add675e45d
commit ec4ec53545
88 changed files with 4050 additions and 872 deletions
@@ -8,11 +8,25 @@ public static class GameplayClock
private static double pauseStartDspTime;
private static double accumulatedPauseSeconds;
private static float chartStartSongTime;
private static double scheduledMusicStartDsp; // anchor: when music actually starts playing
// Seconds the chart clock leads the music clip position by (== globalDelaySeconds + userAudioOffset).
// Captured when music is scheduled; used to preserve the songTime = musicTime + offset relationship
// when re-anchoring to actual music progress after a pause.
private static double musicToChartOffsetSeconds;
// Per-frame cache of NowSongTime. AudioSettings.dspTime only advances on the audio
// thread (~once per audio buffer), so it is effectively constant within a rendered
// frame. Many notes/segments read NowSongTime every frame; caching by frame turns
// hundreds of native dspTime reads per frame into one. Invalidated when the frame
// changes or clock state mutates (Reset/StartChart/Pause/Resume).
private static int nowSongTimeFrame = -1;
private static float nowSongTimeCached;
public static bool IsInitialized => initialized;
public static bool IsPaused => paused;
public static float ChartStartSongTime => chartStartSongTime;
public static double ChartStartDspTime => chartStartDspTime;
public static double ScheduledMusicStartDsp => scheduledMusicStartDsp;
public static void Reset()
{
@@ -22,6 +36,9 @@ public static class GameplayClock
pauseStartDspTime = 0d;
accumulatedPauseSeconds = 0d;
chartStartSongTime = 0f;
scheduledMusicStartDsp = 0d;
musicToChartOffsetSeconds = 0d;
nowSongTimeFrame = -1;
}
public static void StartChart(float initialSongTimeSeconds = 0f)
@@ -32,6 +49,27 @@ public static class GameplayClock
pauseStartDspTime = 0d;
accumulatedPauseSeconds = 0d;
chartStartSongTime = Mathf.Max(0f, initialSongTimeSeconds);
scheduledMusicStartDsp = 0d; // will be set by SetMusicAnchor when music plays
musicToChartOffsetSeconds = 0d;
nowSongTimeFrame = -1;
}
/// <summary>
/// Record the scheduled music start time for reference.
/// Call this immediately after PlayScheduled or Play.
/// NOTE: Does NOT re-anchor chartStartDspTime — the chart clock stays pinned to gameplay start (StartChart),
/// and music simply plays offset into the future. Re-anchoring would shift all note timing by globalDelaySeconds.
/// </summary>
public static void SetMusicAnchor(double musicStartDspTime)
{
if (!initialized) return;
scheduledMusicStartDsp = musicStartDspTime;
// Do NOT re-anchor chartStartDspTime here — note spawn/hit times were computed from the original anchor.
// Re-anchoring would create a systematic offset equal to globalDelaySeconds.
// Capture the constant lead of chart song-time over music-clip time: songTime(D) - musicTime(D),
// where musicTime(D) = D - musicStartDspTime. This lets ReanchorToMusicTime preserve the relationship.
musicToChartOffsetSeconds = chartStartSongTime - chartStartDspTime - accumulatedPauseSeconds + musicStartDspTime;
nowSongTimeFrame = -1;
}
public static void Pause()
@@ -43,6 +81,7 @@ public static class GameplayClock
paused = true;
pauseStartDspTime = AudioSettings.dspTime;
nowSongTimeFrame = -1;
}
public static void Resume()
@@ -55,6 +94,24 @@ public static class GameplayClock
accumulatedPauseSeconds += AudioSettings.dspTime - pauseStartDspTime;
pauseStartDspTime = 0d;
paused = false;
nowSongTimeFrame = -1;
}
/// <summary>
/// Re-anchor the clock to the actual music progress on resume.
/// Call this after Resume() if musicSource.time is available, to correct any drift.
/// </summary>
public static void ReanchorToMusicTime(float actualMusicTime)
{
if (!initialized || paused) return;
// Reverse-calculate chartStartDspTime so that song-time now equals the music clip position
// PLUS the constant lead (globalDelaySeconds/audio offset) captured at SetMusicAnchor.
// Without adding musicToChartOffsetSeconds, resuming would force songTime == musicTime and
// reintroduce the exact backward offset the SetMusicAnchor fix removed.
double now = AudioSettings.dspTime;
double targetSongTime = actualMusicTime + musicToChartOffsetSeconds;
chartStartDspTime = now - accumulatedPauseSeconds - (targetSongTime - chartStartSongTime);
nowSongTimeFrame = -1;
}
public static float NowSongTime
@@ -66,9 +123,21 @@ public static class GameplayClock
return 0f;
}
// While paused the value is frozen (pauseStartDspTime), so it's trivially stable;
// while playing, reuse the value already computed this frame.
int frame = Time.frameCount;
if (frame == nowSongTimeFrame)
{
return nowSongTimeCached;
}
double currentDsp = paused ? pauseStartDspTime : AudioSettings.dspTime;
double elapsed = currentDsp - chartStartDspTime - accumulatedPauseSeconds;
return chartStartSongTime + Mathf.Max(0f, (float)elapsed);
float result = chartStartSongTime + Mathf.Max(0f, (float)elapsed);
nowSongTimeFrame = frame;
nowSongTimeCached = result;
return result;
}
}