fix(engine): emit RequestRedraw from animation systems — restores 60 fps card animation on Android

Commit 38e4c03 (shipped in v0.40.0) switched Android's focused_mode to
reactive_low_power(100 ms) on the premise that every animation tick
system writes RequestRedraw while it has active work. The writers were
never actually added — the diff only contained the WinitSettings change,
imports, and add_message registrations. Result: with no touch input,
nothing wakes the winit loop during a card slide except the 100 ms
fallback ceiling, so animations render at ~10 fps on Android. Desktop
and web keep Continuous mode, which is why only Android was affected
(reported by Rhys the day v0.40.0 shipped; confirmed absent on v0.39.1).

Adds the missing MessageWriter<RequestRedraw> to all seven systems the
original commit message named:
- advance_card_animations (CardAnimationPlugin)
- advance_card_anims (AnimationPlugin — deal/win cascade/slides)
- tick_shake_anim, tick_settle_anim, tick_foundation_flourish
  (FeedbackAnimPlugin)
- drive_toast_display (AnimationPlugin — toast countdown)
- drive_auto_complete (AutoCompletePlugin — step-interval keepalive)

Each writes one RequestRedraw per frame while active work exists
(including delay phases, which also need per-frame ticks). Regression
test asserts an active CardAnimation emits RequestRedraw and an idle
board does not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-07-09 11:25:23 -07:00
parent d179d9d582
commit b5c1ba4867
5 changed files with 89 additions and 0 deletions
@@ -276,10 +276,16 @@ fn tick_shake_anim(
time: Res<Time>,
paused: Option<Res<PausedResource>>,
mut anims: Query<(Entity, &mut Transform, &mut ShakeAnim)>,
mut redraw: MessageWriter<RequestRedraw>,
) {
if paused.is_some_and(|p| p.0) {
return;
}
// Sustain full-rate frames for the shake under Android's
// reactive_low_power focused_mode.
if !anims.is_empty() {
redraw.write(RequestRedraw);
}
let dt = time.delta_secs();
for (entity, mut transform, mut anim) in &mut anims {
anim.elapsed += dt;
@@ -356,10 +362,16 @@ fn tick_settle_anim(
time: Res<Time>,
paused: Option<Res<PausedResource>>,
mut anims: Query<(Entity, &mut Transform, &mut SettleAnim)>,
mut redraw: MessageWriter<RequestRedraw>,
) {
if paused.is_some_and(|p| p.0) {
return;
}
// Sustain full-rate frames for the settle bounce under Android's
// reactive_low_power focused_mode.
if !anims.is_empty() {
redraw.write(RequestRedraw);
}
let dt = time.delta_secs();
for (entity, mut transform, mut anim) in &mut anims {
anim.elapsed += dt;
@@ -581,10 +593,16 @@ fn tick_foundation_flourish(
(Entity, &mut Sprite, &mut FoundationMarkerFlourish),
Without<FoundationFlourish>,
>,
mut redraw: MessageWriter<RequestRedraw>,
) {
if paused.is_some_and(|p| p.0) {
return;
}
// Sustain full-rate frames for the flourish under Android's
// reactive_low_power focused_mode.
if !card_anims.is_empty() || !marker_anims.is_empty() {
redraw.write(RequestRedraw);
}
let dt = time.delta_secs();
// Advance the King's scale pulse.