Skip to content

gameplay: fix crossover cue fade timing and crossfade behavior#597

Draft
adstep wants to merge 3 commits into
pnn64:mainfrom
adstep:adstep-crossover-cue-fade-overlap
Draft

gameplay: fix crossover cue fade timing and crossfade behavior#597
adstep wants to merge 3 commits into
pnn64:mainfrom
adstep:adstep-crossover-cue-fade-overlap

Conversation

@adstep

@adstep adstep commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Why

Crossover cues were recently ported from the custom Simply Love 8ms theme, and a user reported that the fade-out of one cue and the fade-in of the next did not overlap enough. Digging in showed the port split a single fade constant into two mismatched ones and changed the rendering model in ways that quietly broke the intended crossfade.

What changed

Three focused fixes to how crossover cues display (in deadsync-gameplay and deadsync-notefield):

  1. Match the 8ms fade time. The cue builder overlaps consecutive cues by CROSSOVER_CUE_FADE_SECONDS (0.075s), but the renderer faded each cue in/out over 0.15s, twice the overlap. Set the render fade to 0.075s so a cue's fade-out fully crossfades with the next cue's fade-in.

  2. Render every active cue. The render path used active_column_cue, which returns only the single most-recently-started cue, so the moment the next cue began the previous cue's fade-out was dropped. It now draws every cue whose [start, start+duration] window contains the playhead (at most two by construction), producing a true crossfade that matches the 8ms theme's independent per-column actors.

  3. Fade seeked-into cues from the landing point. Instead of popping a cue in at full alpha when you seek into its middle in practice mode, each cue's fade-in is anchored to the point it first becomes visible: its own start during normal play, or the seek-landing time when you jump into it. Normal play is byte-for-byte unchanged.

adstep added 3 commits June 27, 2026 21:17
The cue builder overlaps consecutive crossover cues by
CROSSOVER_CUE_FADE_SECONDS (0.075s), but the renderer faded each cue in and
out over COLUMN_CUE_FADE_TIME = 0.15s — twice the overlap. With the fade
longer than the overlap, the outgoing cue's fade-out never lined up with the
incoming cue's fade-in, so back-to-back cues looked like they barely
overlapped.

Set COLUMN_CUE_FADE_TIME to 0.075s to match the original Simply Love 8ms
theme's fadeTime (and the builder's overlap), so a cue's fade-out fully
crossfades with the next cue's fade-in. Updates the column_cue_alpha tests for
the new timing; notefield tests pass.
…crossfade


The crossover render path used active_column_cue, which returns only the single
most-recently-started cue. The instant the next cue started (at prev_end -
fade) the previous cue stopped rendering, dropping its entire fade-out and
defeating the crossfade the builder sets up.

Add active_column_cues, returning every cue whose [start, start+duration]
window contains the current time, and loop over them in the crossover render
path so the outgoing fade-out and incoming fade-in draw simultaneously —
matching the 8ms theme, where each cue animates on its own independent column
actor. At most two cues overlap by construction. Adds a unit test; cue tests
pass and the workspace builds clean.
deadsync's crossover cue rendering is time-based, so it can show a cue's true
state at any playhead position — something the 8ms theme's fire-and-forget
tweens could not, which is why that theme simply suppresses a cue whose start
you seek past.

Instead of suppressing, anchor each cue's fade-in to the point it first becomes
visible: its own start during normal play, or the seek-landing time when the
playhead jumps into the middle of it. The cue then ramps up from 0 instead of
popping in at full alpha, while still fading out at its real end. Normal play
is unchanged (anchor == start), and anchors clear when the playhead rewinds
before a cue's start so replays fade in naturally.

Adds column_cue_alpha_anchored (column_cue_alpha now delegates to it) and
stores per-cue entry times in the cue runtime, advanced once per player per
frame in update_song_position_from_time. New tests cover the anchor tracking
and the no-pop fade-in; workspace builds clean.
@adstep adstep force-pushed the adstep-crossover-cue-fade-overlap branch from 69a0f48 to 8e345f1 Compare June 28, 2026 04:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant