Fix fighter visuals after landing snap (#67)
CI / build-and-test (push) Successful in 1m47s
CI / production-build (push) Successful in 11m8s

## Summary

- Mark landing position corrections as presentation discontinuities.
- Render and frame fighters from the current position for the snap frame.
- Keep previous translation history unchanged for gameplay collision and attack logic.

Reviewed-on: #67
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #67.
This commit is contained in:
2026-09-26 15:54:25 +00:00
committed by EIREXE
parent 62aa598324
commit 94e292026e
6 changed files with 69 additions and 8 deletions
+42 -2
View File
@@ -21,6 +21,11 @@ pub struct FighterVelocity(pub FGVec2);
#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)]
pub struct FighterPreviousTranslation(pub FGVec2);
/// Marks a simulation frame whose position changed discontinuously for
/// presentation purposes (for example, a landing correction).
#[derive(Component, Default, Clone, Copy, Hash, Debug, PartialEq, Eq)]
pub struct FighterPresentationSnap(pub bool);
#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)]
#[require(FighterPreviousTranslation)]
pub struct FighterTranslation(pub FGVec2);
@@ -52,10 +57,31 @@ pub fn sample_presentation_translation(
}
}
pub fn sample_presentation_translation_with_snap(
previous: FighterPreviousTranslation,
current: FighterTranslation,
overstep_fraction: f32,
mode: PresentationMotionMode,
snapped: FighterPresentationSnap,
) -> Vec2 {
if snapped.0 {
Vec2::new(current.x.to_num(), current.y.to_num())
} else {
sample_presentation_translation(previous, current, overstep_fraction, mode)
}
}
pub fn clear_fighter_presentation_snaps(mut fighters: Query<&mut FighterPresentationSnap>) {
for mut snap in &mut fighters {
snap.0 = false;
}
}
pub fn apply_fighter_translation_to_visuals(
query: Query<(
&FighterTranslation,
&FighterPreviousTranslation,
&FighterPresentationSnap,
&FighterHitstopShake,
&mut Transform,
)>,
@@ -64,12 +90,13 @@ pub fn apply_fighter_translation_to_visuals(
game_settings: Res<GameSettings>,
) {
let overstep_fraction = timing.overstep_fraction();
for (translation, previous_translation, hitstop_shake, mut transform) in query {
let translation = sample_presentation_translation(
for (translation, previous_translation, presentation_snap, hitstop_shake, mut transform) in query {
let translation = sample_presentation_translation_with_snap(
*previous_translation,
*translation,
overstep_fraction,
debug_settings.motion_sampling,
*presentation_snap,
);
transform.translation = Vec3::new(
translation.x,
@@ -169,6 +196,19 @@ mod tests {
assert_eq!(position, Vec2::new(7.0, 14.0));
}
#[test]
fn presentation_translation_uses_current_sample_after_snap() {
let position = sample_presentation_translation_with_snap(
FighterPreviousTranslation(FGVec2::new(FGi32::lit("2"), FGi32::lit("4"))),
FighterTranslation(FGVec2::new(FGi32::lit("6"), FGi32::lit("12"))),
0.25,
PresentationMotionMode::Extrapolation,
FighterPresentationSnap(true),
);
assert_eq!(position, Vec2::new(6.0, 12.0));
}
#[test]
fn snapshot_fighter_motion_copies_current_world_state() {
let mut world = World::new();