From c20bd94b79d560d5df706abd165809a237ccdd13 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Wed, 6 Aug 2025 07:33:05 -0700 Subject: [PATCH] Preserving more data, but rewind backfilling still not working... --- src/bin/flappy/main.rs | 150 +++++++++++++++++++++++++++-------------- src/debug.rs | 39 +++++------ 2 files changed, 120 insertions(+), 69 deletions(-) diff --git a/src/bin/flappy/main.rs b/src/bin/flappy/main.rs index 911d1a3..3e57344 100644 --- a/src/bin/flappy/main.rs +++ b/src/bin/flappy/main.rs @@ -80,7 +80,9 @@ fn main() { sync_resource_to_ui::.run_if(resource_changed::), ), scoring.run_if(on_event::), - manage_batches.run_if(on_event::).run_if(in_state(PlayerState::Alive).or(in_state(PlayerState::Rewind))), + manage_batches + .run_if(on_event::) + .run_if(in_state(PlayerState::Alive).or(in_state(PlayerState::Rewind))), update_batch_position.run_if(any_component_changed::), update_tooltip.run_if(in_state(DebuggingState::On)), ), @@ -114,10 +116,15 @@ enum PlayerState { // A tape tracking the bird's state every frame #[derive(Component, Default)] struct Tape { - translations: Vec, - rotations: Vec, + accumulated_translations: Vec, linear_velocities: Vec, angular_velocities: Vec, + external_angular_impulses: Vec, + external_forces: Vec, + external_impulses: Vec, + external_torques: Vec, + positions: Vec, + rotations: Vec, } fn init_bird( @@ -176,9 +183,7 @@ struct Hitbox; #[derive(Component, Clone)] struct Batch(usize); -fn init_first_batches( - mut commands: Commands -) { +fn init_first_batches(mut commands: Commands) { commands.spawn(Batch(0)); commands.spawn(Batch(1)); commands.spawn(Batch(2)); @@ -205,7 +210,10 @@ fn populate_batch( let Batch(batch_id) = batches.get(trigger.target()).unwrap(); commands .entity(trigger.target()) - .insert((Transform::from_xyz(500.0 * (*batch_id) as f32, 0.0, 0.0), Visibility::Inherited)) + .insert(( + Transform::from_xyz(500.0 * (*batch_id) as f32, 0.0, 0.0), + Visibility::Inherited, + )) .with_children(|parent| { parent.spawn(Ground(-2)); parent.spawn(Ground(-1)); @@ -224,7 +232,7 @@ fn populate_batch( fn update_batch_position( mut root_changes: Query<(&mut Transform, &Batch), (Changed, Without)>, ) { - root_changes.iter_mut().for_each(|(mut t, Batch(idx))|{ + root_changes.iter_mut().for_each(|(mut t, Batch(idx))| { info!("Updating batch {:?}", idx); t.translation.x = 500.0 * (*idx) as f32; }); @@ -245,8 +253,9 @@ fn populate_ground( ground_assets.material.clone(), ground_assets.mesh.clone(), Name::new("ground"), - RigidBody::Static, Collider::rectangle(1.0, 1.0), - Transform::from_xyz(100.0 * (*idx) as f32, -300.0, -1.0).with_scale(Vec3::splat(100.0)) + RigidBody::Static, + Collider::rectangle(1.0, 1.0), + Transform::from_xyz(100.0 * (*idx) as f32, -300.0, -1.0).with_scale(Vec3::splat(100.0)), )); } @@ -266,17 +275,15 @@ fn populate_pipe( pipe_t, pipe_assets.material.clone(), pipe_assets.mesh.clone(), - RigidBody::Static, Collider::rectangle(1.0, 1.0), + RigidBody::Static, + Collider::rectangle(1.0, 1.0), Name::new("pipe"), )); } /// The hitbox should cover the entire height of the screen so if the player /// passes between the pipes it registers a point -fn populate_hitbox( - trigger: Trigger, - mut commands: Commands, -) { +fn populate_hitbox(trigger: Trigger, mut commands: Commands) { commands.entity(trigger.target()).insert(( RigidBody::Static, Collider::rectangle(1.0, 10.0), @@ -502,7 +509,21 @@ fn toggle_rewind(keycode: Res>, mut next: ResMut>, - mut birds: Query<(&Transform, &LinearVelocity, &AngularVelocity, &mut Tape), With>, + mut birds: Query< + ( + &AccumulatedTranslation, + &LinearVelocity, + &AngularVelocity, + &ExternalAngularImpulse, + &ExternalForce, + &ExternalImpulse, + &ExternalTorque, + &Position, + &Rotation, + &mut Tape, + ), + With, + >, ) { debug_assert!( matches!(state.get(), PlayerState::Alive), @@ -511,11 +532,16 @@ fn record( birds .iter_mut() - .for_each(|(transform, linear_velocity, angular_velocity, mut tape)| { - tape.translations.push(transform.translation); - tape.rotations.push(transform.rotation); - tape.linear_velocities.push(*linear_velocity); - tape.angular_velocities.push(*angular_velocity); + .for_each(|(at, lv, av, eai, ef, ei, et, p, r, mut tape)| { + tape.accumulated_translations.push(*at); + tape.linear_velocities.push(*lv); + tape.angular_velocities.push(*av); + tape.external_angular_impulses.push(*eai); + tape.external_forces.push(*ef); + tape.external_impulses.push(*ei); + tape.external_torques.push(*et); + tape.positions.push(*p); + tape.rotations.push(*r); }); } @@ -523,9 +549,15 @@ fn rewind( #[cfg(debug_assertions)] state: Res>, mut birds: Query< ( - &mut Transform, - &mut AngularVelocity, + &mut AccumulatedTranslation, &mut LinearVelocity, + &mut AngularVelocity, + &mut ExternalAngularImpulse, + &mut ExternalForce, + &mut ExternalImpulse, + &mut ExternalTorque, + &mut Position, + &mut Rotation, &mut Tape, ), With, @@ -538,22 +570,24 @@ fn rewind( ); birds.iter_mut().for_each( - |(mut transform, mut angular_velocity, mut linear_velocity, mut tape)| { - match (tape.translations.pop(), tape.rotations.pop()) { - (Some(t), Some(r)) => { - transform.translation = t; - transform.rotation = r; - frames.0 += 1; - } - (None, None) => (), - _ => panic!("Translations and rotations are out of sync!"), - } - // TODO: Only need to set {angular|linear}_velocity at end of Rewind - if let Some(av) = tape.angular_velocities.pop() { - *angular_velocity = av; - } - if let Some(lv) = tape.linear_velocities.pop() { - *linear_velocity = lv; + |(mut at, mut lv, mut av, mut eai, mut ef, mut ei, mut et, mut p, mut r, mut tape)| { + if !tape.positions.is_empty() { + at.0 = tape.accumulated_translations.pop().unwrap().0; + lv.0 = tape.linear_velocities.pop().unwrap().0; + av.0 = tape.angular_velocities.pop().unwrap().0; + eai.set_impulse(tape.external_angular_impulses.pop().unwrap().impulse()); + ef.set_force(tape.external_forces.pop().unwrap().force()); + ei.set_impulse(tape.external_impulses.pop().unwrap().impulse()); + et.set_torque(tape.external_torques.pop().unwrap().torque()); + p.0 = tape.positions.pop().unwrap().0; + *r = { + let curr = tape.rotations.pop().unwrap(); + Rotation { + cos: curr.cos, + sin: curr.sin, + } + }; + frames.0 += 1; } }, ); @@ -593,7 +627,9 @@ fn pause_bird( debug_assert!(!matches!(state.get(), PlayerState::Alive)); // Increment death count - if state.get() == &PlayerState::Stasis { deaths.0 += 1 } + if state.get() == &PlayerState::Stasis { + deaths.0 += 1 + } debug!("Setting bird to Static"); **bird = RigidBody::Static; @@ -673,32 +709,46 @@ fn manage_batches( batches: Query<(Entity, &Batch)>, mut commands: Commands, ) { - debug_assert!(matches!(state.get(), PlayerState::Alive) || matches!(state.get(), PlayerState::Rewind)); + debug_assert!( + matches!(state.get(), PlayerState::Alive) || matches!(state.get(), PlayerState::Rewind) + ); events .read() // This is written in a wonky way to avoid borrow checker rules // Instaed of updating the Batch in place we use Commands to get it and upsert a new batch .filter_map(|CollisionStarted(a, b)| { - if bird.contains(*a) && let Ok((e, Batch(idx))) = batches.get(*b) && *idx > 2 { + if bird.contains(*a) + && let Ok((e, Batch(idx))) = batches.get(*b) + && *idx > 2 + { Some((e, idx)) - } else { None } - }).for_each(|(_, idx)| { + } else { + None + } + }) + .for_each(|(_, idx)| { let (old_batch_idx, new_batch_idx) = match state.get() { PlayerState::Alive => (idx - 2, idx + 2), PlayerState::Rewind => (idx + 2, idx - 2), _ => panic!("Should not happen!"), }; // Find all entities with the old batch and adjust them - batches.iter().filter_map(|(e, batch)| { - (batch.0 == old_batch_idx).then_some(e) - }).for_each(|e| { - commands.entity(e).insert(Batch(new_batch_idx)); - }) + batches + .iter() + .filter_map(|(e, batch)| (batch.0 == old_batch_idx).then_some(e)) + .for_each(|e| { + commands.entity(e).insert(Batch(new_batch_idx)); + }) }); } fn update_tooltip( - mut query: Query<(&mut ToolTip, Option<&LinearVelocity>, Option<&Batch>, Entity)>, + mut query: Query<( + &mut ToolTip, + Option<&LinearVelocity>, + Option<&Batch>, + Entity, + )>, ) { query.iter_mut().for_each(|(mut tt, lv, b, _e)| { // Add Linear Velocity if present on entity diff --git a/src/debug.rs b/src/debug.rs index 395e6c0..a025dd0 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -18,9 +18,7 @@ impl Plugin for DebuggingPlugin { .add_systems( Update, ( - ( - toggle_state_visibility::, - ) + (toggle_state_visibility::,) .run_if(state_changed::), toggle_debug_state.run_if(input_just_pressed(KeyCode::F12)), (toggle_light_gizmo, toggle_aabb_gizmo).run_if(state_changed::), @@ -242,11 +240,7 @@ fn tooltip_follow( } /// Lok tooltip to entity by inserting the Tooltip component on it -fn lock_tooltip( - q: Query>, - hover_map: Res, - mut commands: Commands, -) { +fn lock_tooltip(q: Query>, hover_map: Res, mut commands: Commands) { hover_map.0.iter().for_each(|(_, hit_data_map)| { if let Some((e, _)) = hit_data_map.iter().min_by(|(_, a), (_, b)| { if a.depth > b.depth { @@ -266,17 +260,14 @@ fn lock_tooltip( info!("Removing tooltip from {:?}", e); commands.entity(*e).remove::(); } - _ => () + _ => (), } } }); } /// when a tooltip is added to an entity, insert the text component -fn add_tooltip_text( - trigger: Trigger, - mut commands: Commands, -) { +fn add_tooltip_text(trigger: Trigger, mut commands: Commands) { info!("Adding text2d to {:?}", trigger.target()); commands.entity(trigger.target()).with_children(|parent| { parent.spawn(( @@ -296,15 +287,25 @@ fn populate_tooltip_info( if let Ok((mut tt, e, t, n)) = tooltips.get_mut(trigger.target()) { tt.insert("ID", format!("{e}")); tt.insert("Name", n.into()); - tt.insert("Pos", format!("{:.2?} {:.2?} {:.2?}", t.translation.x, t.translation.y, t.translation.z)); + tt.insert( + "Pos", + format!( + "{:.2?} {:.2?} {:.2?}", + t.translation.x, t.translation.y, t.translation.z + ), + ); } } -fn tooltip_update_transform( - mut events: Query<(&mut ToolTip, &Transform), Changed>, -) { +fn tooltip_update_transform(mut events: Query<(&mut ToolTip, &Transform), Changed>) { events.iter_mut().for_each(|(mut tt, t)| { - tt.insert("Pos", format!("{:.2?} {:.2?} {:.2?}", t.translation.x, t.translation.y, t.translation.z)); + tt.insert( + "Pos", + format!( + "{:.2?} {:.2?} {:.2?}", + t.translation.x, t.translation.y, t.translation.z + ), + ); }); } @@ -333,7 +334,7 @@ fn tooltip_draw( // Find the tooltip text child children.iter().for_each(|child| { if let Ok(mut text) = text_2d.get_mut(child) { - text.0 = format!("{}", tt); + text.0 = format!("{tt}"); } }); })