Fix fighter visuals after landing snap #67
+9
-5
@@ -5,7 +5,7 @@ use bevy_ggrs::GgrsFrameTiming;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
debug_tools::DebugSettings,
|
debug_tools::DebugSettings,
|
||||||
fighter::{manifest::FighterManifest, motion::sample_presentation_translation},
|
fighter::{manifest::FighterManifest, motion::{sample_presentation_translation_with_snap, FighterPresentationSnap}},
|
||||||
stage::manifest::StageCameraProfile,
|
stage::manifest::StageCameraProfile,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -313,6 +313,7 @@ fn update_match_camera(
|
|||||||
&crate::fighter::motion::FighterPreviousTranslation,
|
&crate::fighter::motion::FighterPreviousTranslation,
|
||||||
&crate::fighter::Fighter,
|
&crate::fighter::Fighter,
|
||||||
&crate::fighter::FighterFacingDirection,
|
&crate::fighter::FighterFacingDirection,
|
||||||
|
&FighterPresentationSnap,
|
||||||
&mut FighterCameraExtents,
|
&mut FighterCameraExtents,
|
||||||
),
|
),
|
||||||
With<crate::player::Player>,
|
With<crate::player::Player>,
|
||||||
@@ -329,16 +330,17 @@ fn update_match_camera(
|
|||||||
let subjects = fighters
|
let subjects = fighters
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(
|
.map(
|
||||||
|(translation, previous_translation, fighter, facing, mut extents)| {
|
|(translation, previous_translation, fighter, facing, presentation_snap, mut extents)| {
|
||||||
let manifest = manifests
|
let manifest = manifests
|
||||||
.get(&fighter.manifest)
|
.get(&fighter.manifest)
|
||||||
.expect("Fighter manifest should be valid");
|
.expect("Fighter manifest should be valid");
|
||||||
fighter_subject_bounds(
|
fighter_subject_bounds(
|
||||||
sample_presentation_translation(
|
sample_presentation_translation_with_snap(
|
||||||
*previous_translation,
|
*previous_translation,
|
||||||
*translation,
|
*translation,
|
||||||
timing.overstep_fraction(),
|
timing.overstep_fraction(),
|
||||||
debug_settings.motion_sampling,
|
debug_settings.motion_sampling,
|
||||||
|
*presentation_snap,
|
||||||
),
|
),
|
||||||
manifest.camera,
|
manifest.camera,
|
||||||
*facing,
|
*facing,
|
||||||
@@ -465,6 +467,7 @@ fn debug_draw_camera(
|
|||||||
&crate::fighter::motion::FighterPreviousTranslation,
|
&crate::fighter::motion::FighterPreviousTranslation,
|
||||||
&crate::fighter::Fighter,
|
&crate::fighter::Fighter,
|
||||||
&crate::fighter::FighterFacingDirection,
|
&crate::fighter::FighterFacingDirection,
|
||||||
|
&FighterPresentationSnap,
|
||||||
&FighterCameraExtents,
|
&FighterCameraExtents,
|
||||||
),
|
),
|
||||||
With<crate::player::Player>,
|
With<crate::player::Player>,
|
||||||
@@ -486,15 +489,16 @@ fn debug_draw_camera(
|
|||||||
Color::srgb(1.0, 0.5, 0.0),
|
Color::srgb(1.0, 0.5, 0.0),
|
||||||
);
|
);
|
||||||
draw_rectangle(&mut gizmos, camera.frame, Color::srgb(1.0, 1.0, 0.0));
|
draw_rectangle(&mut gizmos, camera.frame, Color::srgb(1.0, 1.0, 0.0));
|
||||||
for (translation, previous_translation, fighter, _facing, extents) in &fighters {
|
for (translation, previous_translation, fighter, _facing, presentation_snap, extents) in &fighters {
|
||||||
let manifest = manifests
|
let manifest = manifests
|
||||||
.get(&fighter.manifest)
|
.get(&fighter.manifest)
|
||||||
.expect("Fighter manifest should be valid");
|
.expect("Fighter manifest should be valid");
|
||||||
let center = sample_presentation_translation(
|
let center = sample_presentation_translation_with_snap(
|
||||||
*previous_translation,
|
*previous_translation,
|
||||||
*translation,
|
*translation,
|
||||||
timing.overstep_fraction(),
|
timing.overstep_fraction(),
|
||||||
debug_settings.motion_sampling,
|
debug_settings.motion_sampling,
|
||||||
|
*presentation_snap,
|
||||||
) + Vec2::Y * manifest.camera.vertical_origin_offset.to_num::<f32>();
|
) + Vec2::Y * manifest.camera.vertical_origin_offset.to_num::<f32>();
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
&mut gizmos,
|
&mut gizmos,
|
||||||
|
|||||||
+9
-1
@@ -29,7 +29,8 @@ pub use attributes::FighterAttributes;
|
|||||||
pub use ecb::FighterECB;
|
pub use ecb::FighterECB;
|
||||||
pub use manifest::FighterId;
|
pub use manifest::FighterId;
|
||||||
pub use motion::{
|
pub use motion::{
|
||||||
FighterPreviousFacingDirection, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
|
FighterPresentationSnap, FighterPreviousFacingDirection, FighterPreviousTranslation,
|
||||||
|
FighterTranslation, FighterVelocity,
|
||||||
};
|
};
|
||||||
pub use surface::FighterSurfaceState;
|
pub use surface::FighterSurfaceState;
|
||||||
|
|
||||||
@@ -147,6 +148,10 @@ impl Plugin for FighterPlugin {
|
|||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins(FighterAttackPlugin)
|
app.add_plugins(FighterAttackPlugin)
|
||||||
// Every state gets a chance to interrupt itself, in priority order.
|
// Every state gets a chance to interrupt itself, in priority order.
|
||||||
|
.add_systems(
|
||||||
|
GgrsSchedule,
|
||||||
|
motion::clear_fighter_presentation_snaps.in_set(GameplaySet::Input),
|
||||||
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
GgrsSchedule,
|
GgrsSchedule,
|
||||||
(
|
(
|
||||||
@@ -202,6 +207,8 @@ impl Plugin for FighterPlugin {
|
|||||||
.checksum_component_with_hash::<FighterTranslation>()
|
.checksum_component_with_hash::<FighterTranslation>()
|
||||||
.rollback_component_with_copy::<FighterPreviousTranslation>()
|
.rollback_component_with_copy::<FighterPreviousTranslation>()
|
||||||
.checksum_component_with_hash::<FighterPreviousTranslation>()
|
.checksum_component_with_hash::<FighterPreviousTranslation>()
|
||||||
|
.rollback_component_with_copy::<FighterPresentationSnap>()
|
||||||
|
.checksum_component_with_hash::<FighterPresentationSnap>()
|
||||||
.rollback_component_with_copy::<FighterPreviousFacingDirection>()
|
.rollback_component_with_copy::<FighterPreviousFacingDirection>()
|
||||||
.checksum_component_with_hash::<FighterPreviousFacingDirection>()
|
.checksum_component_with_hash::<FighterPreviousFacingDirection>()
|
||||||
.rollback_component_with_copy::<animation::FighterAnimationFrame>()
|
.rollback_component_with_copy::<animation::FighterAnimationFrame>()
|
||||||
@@ -304,6 +311,7 @@ pub fn spawn_fighter(
|
|||||||
FighterVelocity::default(),
|
FighterVelocity::default(),
|
||||||
FighterTranslation(spawn_position),
|
FighterTranslation(spawn_position),
|
||||||
FighterPreviousTranslation(spawn_position),
|
FighterPreviousTranslation(spawn_position),
|
||||||
|
FighterPresentationSnap::default(),
|
||||||
),
|
),
|
||||||
visual_root,
|
visual_root,
|
||||||
animations,
|
animations,
|
||||||
|
|||||||
+42
-2
@@ -21,6 +21,11 @@ pub struct FighterVelocity(pub FGVec2);
|
|||||||
#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)]
|
#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)]
|
||||||
pub struct FighterPreviousTranslation(pub FGVec2);
|
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)]
|
#[derive(Component, Deref, DerefMut, Default, Clone, Copy, Hash)]
|
||||||
#[require(FighterPreviousTranslation)]
|
#[require(FighterPreviousTranslation)]
|
||||||
pub struct FighterTranslation(pub FGVec2);
|
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(
|
pub fn apply_fighter_translation_to_visuals(
|
||||||
query: Query<(
|
query: Query<(
|
||||||
&FighterTranslation,
|
&FighterTranslation,
|
||||||
&FighterPreviousTranslation,
|
&FighterPreviousTranslation,
|
||||||
|
&FighterPresentationSnap,
|
||||||
&FighterHitstopShake,
|
&FighterHitstopShake,
|
||||||
&mut Transform,
|
&mut Transform,
|
||||||
)>,
|
)>,
|
||||||
@@ -64,12 +90,13 @@ pub fn apply_fighter_translation_to_visuals(
|
|||||||
game_settings: Res<GameSettings>,
|
game_settings: Res<GameSettings>,
|
||||||
) {
|
) {
|
||||||
let overstep_fraction = timing.overstep_fraction();
|
let overstep_fraction = timing.overstep_fraction();
|
||||||
for (translation, previous_translation, hitstop_shake, mut transform) in query {
|
for (translation, previous_translation, presentation_snap, hitstop_shake, mut transform) in query {
|
||||||
let translation = sample_presentation_translation(
|
let translation = sample_presentation_translation_with_snap(
|
||||||
*previous_translation,
|
*previous_translation,
|
||||||
*translation,
|
*translation,
|
||||||
overstep_fraction,
|
overstep_fraction,
|
||||||
debug_settings.motion_sampling,
|
debug_settings.motion_sampling,
|
||||||
|
*presentation_snap,
|
||||||
);
|
);
|
||||||
transform.translation = Vec3::new(
|
transform.translation = Vec3::new(
|
||||||
translation.x,
|
translation.x,
|
||||||
@@ -169,6 +196,19 @@ mod tests {
|
|||||||
assert_eq!(position, Vec2::new(7.0, 14.0));
|
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]
|
#[test]
|
||||||
fn snapshot_fighter_motion_copies_current_world_state() {
|
fn snapshot_fighter_motion_copies_current_world_state() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ pub fn collision_interrupt(
|
|||||||
ctx.ecb.left -= diff;
|
ctx.ecb.left -= diff;
|
||||||
ctx.ecb.right -= diff;
|
ctx.ecb.right -= diff;
|
||||||
ctx.translation.0 = res.hit_position;
|
ctx.translation.0 = res.hit_position;
|
||||||
|
ctx.presentation_snap.0 = true;
|
||||||
ctx.surface.land(res.line_id);
|
ctx.surface.land(res.line_id);
|
||||||
Some(FighterState::Land(LandingState::create(landing_type)))
|
Some(FighterState::Land(LandingState::create(landing_type)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -376,6 +376,7 @@ mod tests {
|
|||||||
right: FGVec2::lit("0.5", "0.0"),
|
right: FGVec2::lit("0.5", "0.0"),
|
||||||
};
|
};
|
||||||
let previous_ecb = FighterPreviousECB::from(ecb);
|
let previous_ecb = FighterPreviousECB::from(ecb);
|
||||||
|
let mut presentation_snap = crate::fighter::FighterPresentationSnap::default();
|
||||||
let mut context = CollisionContext {
|
let mut context = CollisionContext {
|
||||||
translation: &mut translation,
|
translation: &mut translation,
|
||||||
prev_translation: &previous_translation,
|
prev_translation: &previous_translation,
|
||||||
@@ -386,6 +387,7 @@ mod tests {
|
|||||||
prev_ecb: &previous_ecb,
|
prev_ecb: &previous_ecb,
|
||||||
stage_collision: &stage_collision,
|
stage_collision: &stage_collision,
|
||||||
leave_ground_ecb_lock_duration: 10,
|
leave_ground_ecb_lock_duration: 10,
|
||||||
|
presentation_snap: &mut presentation_snap,
|
||||||
downward_stick_threshold: FGi32::ONE,
|
downward_stick_threshold: FGi32::ONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -429,6 +431,7 @@ mod tests {
|
|||||||
right: FGVec2::lit("0.5", "0.0"),
|
right: FGVec2::lit("0.5", "0.0"),
|
||||||
};
|
};
|
||||||
let previous_ecb = FighterPreviousECB::from(ecb);
|
let previous_ecb = FighterPreviousECB::from(ecb);
|
||||||
|
let mut presentation_snap = crate::fighter::FighterPresentationSnap::default();
|
||||||
let mut context = CollisionContext {
|
let mut context = CollisionContext {
|
||||||
translation: &mut translation,
|
translation: &mut translation,
|
||||||
prev_translation: &previous_translation,
|
prev_translation: &previous_translation,
|
||||||
@@ -439,6 +442,7 @@ mod tests {
|
|||||||
prev_ecb: &previous_ecb,
|
prev_ecb: &previous_ecb,
|
||||||
stage_collision: &stage_collision,
|
stage_collision: &stage_collision,
|
||||||
leave_ground_ecb_lock_duration: 10,
|
leave_ground_ecb_lock_duration: 10,
|
||||||
|
presentation_snap: &mut presentation_snap,
|
||||||
downward_stick_threshold: FGi32::ONE,
|
downward_stick_threshold: FGi32::ONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use crate::fighter::{
|
|||||||
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterHitstop,
|
Fighter, FighterECB, FighterFacingDirection, FighterHitboxes, FighterHitstop,
|
||||||
FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole,
|
FighterHitstopShake, FighterHitstopShakeKind, FighterHitstopShakeRole,
|
||||||
FighterPreviousTranslation, FighterSurfaceState, FighterTranslation, FighterVelocity,
|
FighterPreviousTranslation, FighterSurfaceState, FighterTranslation, FighterVelocity,
|
||||||
|
FighterPresentationSnap,
|
||||||
};
|
};
|
||||||
use crate::fighter::surface::EcbLockPolicy;
|
use crate::fighter::surface::EcbLockPolicy;
|
||||||
use crate::game_settings::GameSettings;
|
use crate::game_settings::GameSettings;
|
||||||
@@ -200,6 +201,7 @@ pub struct CollisionContext<'a> {
|
|||||||
pub prev_ecb: &'a FighterPreviousECB,
|
pub prev_ecb: &'a FighterPreviousECB,
|
||||||
pub stage_collision: &'a StageCollision,
|
pub stage_collision: &'a StageCollision,
|
||||||
pub leave_ground_ecb_lock_duration: u32,
|
pub leave_ground_ecb_lock_duration: u32,
|
||||||
|
pub presentation_snap: &'a mut FighterPresentationSnap,
|
||||||
pub downward_stick_threshold: FGi32,
|
pub downward_stick_threshold: FGi32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +246,7 @@ fighter_states! {
|
|||||||
pub struct FighterFrameQuery {
|
pub struct FighterFrameQuery {
|
||||||
pub translation: &'static mut FighterTranslation,
|
pub translation: &'static mut FighterTranslation,
|
||||||
pub prev_translation: &'static mut FighterPreviousTranslation,
|
pub prev_translation: &'static mut FighterPreviousTranslation,
|
||||||
|
pub presentation_snap: &'static mut FighterPresentationSnap,
|
||||||
pub input: &'static mut FighterInput,
|
pub input: &'static mut FighterInput,
|
||||||
pub hitboxes: &'static mut FighterHitboxes,
|
pub hitboxes: &'static mut FighterHitboxes,
|
||||||
pub velocity: &'static mut FighterVelocity,
|
pub velocity: &'static mut FighterVelocity,
|
||||||
@@ -354,6 +357,7 @@ impl<'w, 's> FighterFrameQueryItem<'w, 's> {
|
|||||||
CollisionContext {
|
CollisionContext {
|
||||||
translation: &mut self.translation,
|
translation: &mut self.translation,
|
||||||
prev_translation: &self.prev_translation,
|
prev_translation: &self.prev_translation,
|
||||||
|
presentation_snap: &mut self.presentation_snap,
|
||||||
input: &self.input,
|
input: &self.input,
|
||||||
velocity: &mut self.velocity,
|
velocity: &mut self.velocity,
|
||||||
surface: &mut self.surface,
|
surface: &mut self.surface,
|
||||||
|
|||||||
Reference in New Issue
Block a user