diff --git a/src/fighter/attack/mod.rs b/src/fighter/attack/mod.rs index 6696767..134943e 100644 --- a/src/fighter/attack/mod.rs +++ b/src/fighter/attack/mod.rs @@ -605,7 +605,6 @@ impl Plugin for FighterAttackPlugin { intersect_attacks_with_hurtboxes, ) .chain() - .after(crate::fighter::baked_animation::update_fighter_bone_matrices) .in_set(GameplaySet::Attacks), ); } diff --git a/src/fighter/baked_animation/mod.rs b/src/fighter/baked_animation/mod.rs index d5b6213..1c95527 100644 --- a/src/fighter/baked_animation/mod.rs +++ b/src/fighter/baked_animation/mod.rs @@ -219,6 +219,7 @@ mod tests { second.translate(FGVec3::lit("0", "3", "0")); let mut matrices = FighterBoneMatrices::default(); matrices.set_pose( + AnimKind::Wait, &["root".into(), "hand".into()], &[None, Some(0)], &[first, second], @@ -239,6 +240,7 @@ mod tests { hand.translate(FGVec3::lit("0", "3", "0")); let mut matrices = FighterBoneMatrices::default(); matrices.set_pose( + AnimKind::Wait, &["root".into(), "hand".into()], &[None, Some(0)], &[root, hand], @@ -266,9 +268,9 @@ mod tests { let parents = [None, Some(0)]; let pose = [FixedMat4::IDENTITY, FixedMat4::IDENTITY]; - matrices.set_pose(&names, &parents, &pose, FGi32::ONE); + matrices.set_pose(AnimKind::Wait, &names, &parents, &pose, FGi32::ONE); let capacities = matrices.buffer_capacities(); - matrices.set_pose(&names, &parents, &pose, FGi32::ONE); + matrices.set_pose(AnimKind::Wait, &names, &parents, &pose, FGi32::ONE); assert_eq!(capacities, matrices.buffer_capacities()); } @@ -281,6 +283,7 @@ mod tests { hand.translate(FGVec3::lit("0", "2", "0")); let mut matrices = FighterBoneMatrices::default(); matrices.set_pose( + AnimKind::Wait, &["root".into(), "hand".into()], &[None, Some(0)], &[root, hand], diff --git a/src/fighter/baked_animation/runtime.rs b/src/fighter/baked_animation/runtime.rs index c105f63..18b65b6 100644 --- a/src/fighter/baked_animation/runtime.rs +++ b/src/fighter/baked_animation/runtime.rs @@ -71,6 +71,7 @@ pub struct FighterBoneMatrices { local_matrices: Vec, previous_local_matrices: Vec, model_matrices: Vec>, + presentation_kind: Option, } #[derive(Component, Clone, Debug, Default)] @@ -138,6 +139,7 @@ impl FighterBoneMatrices { } pub(super) fn set_pose( &mut self, + kind: AnimKind, bone_names: &[String], parents: &[Option], local: &[FixedMat4], @@ -166,6 +168,7 @@ impl FighterBoneMatrices { self.previous_local_matrices.reserve(local.len()); self.model_matrices.reserve(local.len()); std::mem::swap(&mut self.matrices, &mut self.previous_frame_matrices); + let animation_changed = self.presentation_kind != Some(kind); std::mem::swap(&mut self.local_matrices, &mut self.previous_local_matrices); self.local_matrices.clear(); self.local_matrices.extend_from_slice(local); @@ -189,6 +192,14 @@ impl FighterBoneMatrices { model_scale.mul(matrix.expect("baked bone hierarchy should be valid")) }), ); + // A state transition is a discontinuity, not an animation frame to + // interpolate from the previous clip. Keep the presentation pose at + // the new clip's first/current frame immediately. + if animation_changed { + self.previous_local_matrices + .clone_from(&self.local_matrices); + } + self.presentation_kind = Some(kind); } } @@ -218,6 +229,7 @@ pub fn update_fighter_bone_matrices( .get(&fighter.manifest) .expect("fighter manifest should be loaded before the match starts"); matrices.set_pose( + frame.kind, &baked.bone_names, &baked.parents, pose, diff --git a/src/fighter/ecb.rs b/src/fighter/ecb.rs index 98358dd..998b38f 100644 --- a/src/fighter/ecb.rs +++ b/src/fighter/ecb.rs @@ -111,10 +111,8 @@ pub fn update_ecb( 0 }; - dbg!(ecb_lock_frames_remaining); - ecb.top = top; - + if ecb_lock_frames_remaining == 0 { ecb.bottom = bottom; } @@ -131,7 +129,7 @@ pub fn update_ecb( pub fn decrement_ecb_lockout(surface_states: Query<&mut FighterSurfaceState>) { for mut state in surface_states { state.tick_ecb_lock(); - } + } } #[cfg(test)] diff --git a/src/fighter/mod.rs b/src/fighter/mod.rs index a429c21..9c24b71 100644 --- a/src/fighter/mod.rs +++ b/src/fighter/mod.rs @@ -171,10 +171,15 @@ impl Plugin for FighterPlugin { )*/ .add_systems( GgrsSchedule, - (ecb::update_ecb, state::state_collision_interrupt_system, ecb::decrement_ecb_lockout).chain().in_set(GameplaySet::Collision), + ( + (ecb::update_ecb, ecb::decrement_ecb_lockout, state::state_collision_interrupt_system).chain(), + baked_animation::update_fighter_bone_matrices, + ) + .chain() + .in_set(GameplaySet::Collision), ) .add_systems( - Update, + PostUpdate, ( motion::apply_fighter_translation_to_visuals, animation::apply_animation, diff --git a/src/schedule.rs b/src/schedule.rs index 3bdb687..7c35a16 100644 --- a/src/schedule.rs +++ b/src/schedule.rs @@ -19,10 +19,10 @@ pub enum GameplaySet { StateUpdate, /// Integrate velocities into positions. Physics, - /// Solve collision boxes - Animation, /// Resolve the integrated positions against the stage. Collision, + /// Solve collision boxes + Animation, /// Resolve attacks Attacks, } @@ -40,8 +40,8 @@ impl Plugin for GameplaySchedulePlugin { GameplaySet::Interrupt, GameplaySet::StateUpdate, GameplaySet::Physics, - GameplaySet::Collision, GameplaySet::Animation, + GameplaySet::Collision, GameplaySet::Attacks, ) .chain(),