Port to fixed point math

This commit is contained in:
2026-08-24 21:54:23 +02:00
parent 91c5c4e05f
commit 73855ea354
27 changed files with 703 additions and 229 deletions
+7 -6
View File
@@ -2,7 +2,7 @@ use std::hash::{Hash, Hasher};
use bevy::prelude::*;
use super::{FighterStateImpl, FighterStateContext, FighterStateTransition, ground, run, wait, walk};
use super::{FighterStateImpl, FighterStateContext, ground, run, wait, walk};
use crate::fighter::state::ground::GroundedStateCommon;
use crate::fighter::state::wait::WaitState;
use crate::fighter::state::{FighterState};
@@ -12,11 +12,12 @@ use crate::input::{
FighterCommands::{SmashMoveLeft, SmashMoveRight},
FighterInput,
};
use crate::math::int::FGi32;
#[derive(Debug, Clone)]
pub struct DashState {
frames_in_dash: u32,
direction: f32,
direction: FGi32,
ground_common: GroundedStateCommon
}
@@ -28,7 +29,7 @@ impl std::hash::Hash for DashState {
}
impl DashState {
pub fn new(direction: f32, ground_common: GroundedStateCommon) -> Self {
pub fn new(direction: FGi32, ground_common: GroundedStateCommon) -> Self {
Self {
frames_in_dash: 0,
direction,
@@ -98,11 +99,11 @@ impl FighterStateImpl for DashState {
}
}
fn check_smash_input_with_dir(state_context: &FighterStateContext) -> Option<f32> {
fn check_smash_input_with_dir(state_context: &FighterStateContext) -> Option<FGi32> {
if state_context.input.has_command(SmashMoveLeft) {
return Some(-1.0);
return Some(FGi32::NEG_ONE);
} else if state_context.input.has_command(SmashMoveRight) {
return Some(1.0);
return Some(FGi32::ONE);
} else {
None
}
+1 -1
View File
@@ -1,6 +1,6 @@
use bevy::prelude::*;
use super::{FighterStateImpl, FighterStateContext, FighterStateTransition, ground, walk};
use super::{FighterStateImpl, FighterStateContext, ground, walk};
use crate::fighter::state::{FighterState, air};
use crate::fighter::state::ground::GroundedStateCommon;
use crate::fighter::state::wait::WaitState;
+21 -23
View File
@@ -7,17 +7,20 @@ use crate::fighter::{
FighterAttributes, FighterECB, FighterPreviousTranslation, FighterTranslation, FighterVelocity,
};
use crate::game_settings::GameSettings;
use crate::math::int::FGi32;
use crate::math::vec::FGVec2;
use crate::stage::StagePoly;
use crate::stage::line::{StageCollision, StageLineID, StagePolyLineSegment, StagePolyLineSegmentType};
use bevy::prelude::*;
use fixed::traits::Fixed;
#[derive(Reflect, Hash, Clone, Debug)]
pub struct GroundedStateCommon {
pub current_line_id: StageLineID,
}
pub fn apply_grounded_friction(friction: f32, ground_vel: f32) -> f32 {
pub fn apply_grounded_friction(friction: FGi32, ground_vel: FGi32) -> FGi32 {
if friction.abs() > ground_vel.abs() {
-ground_vel
} else {
@@ -28,21 +31,21 @@ pub fn apply_grounded_friction(friction: f32, ground_vel: f32) -> f32 {
pub fn move_accelerate() {}
pub fn compute_ground_accel(
accel: f32,
target_vel: f32,
gr_vel: f32,
accel: FGi32,
target_vel: FGi32,
gr_vel: FGi32,
attribs: &FighterAttributes,
game_settings: &GameSettings,
) -> f32 {
) -> FGi32 {
if target_vel == 0.0 {
apply_grounded_friction(attribs.ground_friction, gr_vel)
} else {
let mut accel = accel;
let ground_max_horizontal_velocity =
game_settings.figher_common.ground_max_horizontal_velocity;
if !(gr_vel * accel < 0.0) {
if !(gr_vel * accel < FGi32::ZERO) {
// accelerating, not reversing
if accel > 0.0 {
if accel > FGi32::ZERO {
if gr_vel + accel > target_vel {
accel = -attribs.ground_friction;
if gr_vel + accel < target_vel {
@@ -78,7 +81,7 @@ pub fn apply_grounded_motion(
) {
// Vertical velocity should be 0 on the ground
prev_translation.0 = translation.0;
translation.0 += Vec2::new(velocity.0.x ,0.0);
translation.0 += FGVec2::new(velocity.0.x ,FGi32::ZERO);
}
pub fn collide_with_stage_grounded(
@@ -96,34 +99,29 @@ pub fn collide_with_stage_grounded(
if let Some(stage_poly) = stage_poly {
let ecb_bottom_pos = translation.0 + ecb.get_bottom_point();
let left_segment_idx = ground_common
.current_line_id
.segment
.checked_sub(1)
.unwrap_or(0);
let right_segment_idx =
(ground_common.current_line_id.segment + 1) % stage_poly.segments.len();
let current_segment_idx = ground_common.current_line_id.segment;
// First, we check the current segment, if that fails, we check the surrounding ones
let grounded_ecb_bottom_pos = [
ground_common.current_line_id.segment.clone(),
left_segment_idx,
right_segment_idx,
Some(current_segment_idx),
stage_poly.get_prev_segment_index(current_segment_idx),
stage_poly.get_next_segment_index(current_segment_idx),
]
.iter()
.into_iter()
.flatten()
.find_map(|segment_idx| {
let segment = stage_poly.get_segment(*segment_idx)?;
let segment = stage_poly.get_segment(segment_idx)?;
if segment.segment_type != StagePolyLineSegmentType::Floor {
return None;
}
let line_dir = segment.segment.direction();
let projected_point = segment.segment.point1() + (ecb_bottom_pos - segment.segment.point1()).project_onto(line_dir.as_vec2());
let projected_point = segment.segment.point1() + (ecb_bottom_pos - segment.segment.point1()).project_onto(line_dir);
let closest_point = segment.segment.closest_point(ecb_bottom_pos);
let distance = closest_point.distance(projected_point);
if distance < 0.01 {
return Some((projected_point, *segment_idx, segment.normal));
return Some((projected_point, segment_idx, segment.normal));
}
None
+85 -20
View File
@@ -1,41 +1,106 @@
use bevy::prelude::*;
use crate::fighter::state::{FighterState, FighterStateImpl};
use crate::{
fighter::state::{
FighterState, FighterStateContext, FighterStateImpl,
fall::FallState,
ground::{self, GroundedMotionResult, GroundedStateCommon},
},
input::FighterCommands,
};
pub struct JumpState {
pub counter: u32
pub enum JumpType {
ShortHop,
LongJump,
DoubleJump,
}
#[derive(Debug, Clone, Hash)]
pub struct JumpState {}
#[derive(Debug, Clone, Hash)]
pub struct JumpSquatState {
pub counter: u32
pub grounded_common: GroundedStateCommon,
pub duration_counter: u32,
}
impl JumpSquatState {
pub fn create(grounded_common: GroundedStateCommon) -> Self {
Self {
grounded_common,
duration_counter: 0,
}
}
}
impl FighterStateImpl for JumpSquatState {
const NAME: &'static str = "JumpSquat";
fn check_interrupt(
&self,
state_context: &super::FighterStateContext,
) -> Option<FighterState> {
None
fn check_interrupt(&self, state_context: &FighterStateContext) -> Option<FighterState> {
if self.duration_counter >= state_context.fighter_attribs.jumpsquat_duration {
Some(FighterState::Jump(JumpState {}))
} else {
None
}
}
fn update(&mut self, _state_context: &mut super::FighterStateContext) {
todo!()
fn update(&mut self, state_context: &mut FighterStateContext) {
self.duration_counter += 1;
let friction =
if state_context.velocity.x.abs() > state_context.fighter_attribs.max_walk_vel {
state_context.fighter_attribs.ground_friction
* state_context
.game_settings
.figher_common
.ground_friction_over_walk_speed_multiplier
} else {
state_context.fighter_attribs.ground_friction
};
let ground_velocity = state_context.velocity.x;
state_context.velocity.x += ground::apply_grounded_friction(friction, ground_velocity);
ground::apply_grounded_motion(
state_context.velocity,
state_context.translation,
state_context.prev_translation,
);
}
fn check_collision_interrupt(
&mut self,
state_context: &mut FighterStateContext,
) -> Option<FighterState> {
if let GroundedMotionResult::InAir =
ground::collide_with_stage_grounded(state_context, &mut self.grounded_common, false)
{
Some(FighterState::Fall(FallState))
} else {
None
}
}
}
impl FighterStateImpl for JumpState {
const NAME: &'static str = "Jump";
fn check_interrupt(
&self,
state_context: &super::FighterStateContext,
) -> Option<FighterState> {
fn check_interrupt(&self, state_context: &FighterStateContext) -> Option<FighterState> {
None
}
fn update(&mut self, _state_context: &mut super::FighterStateContext) {
todo!()
}
}
fn update(&mut self, _state_context: &mut FighterStateContext) {}
}
pub fn check_input(
state_context: &FighterStateContext,
ground_common: &GroundedStateCommon,
) -> Option<FighterState> {
state_context
.input
.has_command(FighterCommands::Jump)
.then_some(FighterState::JumpSquat(JumpSquatState::create(
ground_common.clone(),
)));
None
}
+13 -13
View File
@@ -11,6 +11,7 @@ use crate::fighter::ecb::FighterPreviousECB;
use crate::fighter::{FighterAttributes, FighterECB, FighterPreviousTranslation, FighterTranslation, FighterVelocity};
use crate::game_settings::GameSettings;
use crate::input::FighterInput;
use crate::math::int::FGi32;
use crate::schedule::GameplaySet;
use crate::stage::StagePoly;
use crate::stage::line::StageCollision;
@@ -24,14 +25,6 @@ pub mod fall;
pub mod air;
pub mod jump;
pub enum FighterStateTransition {
Wait,
Walk,
Dash(f32),
Run,
Fall,
}
macro_rules! fighter_states {
($($variant:ident => $ty:ty),* $(,)?) => {
#[derive(Component, Clone, Debug, Hash)]
@@ -80,7 +73,7 @@ pub trait FighterStateImpl: Sized {
}
}
pub struct FighterStateContext<'a> {
pub struct FighterStateContext<'a, 'w, 's> {
pub translation: &'a mut FighterTranslation,
pub prev_translation: &'a mut FighterPreviousTranslation,
pub input: &'a mut FighterInput,
@@ -91,6 +84,7 @@ pub struct FighterStateContext<'a> {
pub game_settings: &'a GameSettings,
pub entity: Entity,
pub stage_collision: &'a StageCollision,
pub gizmos: Option<&'a mut Gizmos<'w, 's>>,
}
fighter_states! {
@@ -98,7 +92,9 @@ fighter_states! {
Walk => walk::WalkState,
Dash => dash::DashState,
Run => run::RunState,
Fall => fall::FallState
Fall => fall::FallState,
JumpSquat => jump::JumpSquatState,
Jump => jump::JumpState
}
#[derive(Component)]
@@ -128,6 +124,7 @@ pub fn state_update_system(
game_settings: &game_settings,
entity: ent,
stage_collision: &stage_collision,
gizmos: Some(&mut gizmos),
};
state.update(&mut update_ctx);
}
@@ -150,7 +147,8 @@ pub fn state_interrupt_system (
fighter_attribs: &attribs,
game_settings: &game_settings,
entity: ent,
stage_collision: &stage_collision
stage_collision: &stage_collision,
gizmos: None,
};
if let Some(new_state) = state.check_interrupt(&state_context) {
let mut new_state = new_state;
@@ -171,6 +169,7 @@ pub fn state_collision_interrupt_system(
stage_collision: Res<StageCollision>,
game_settings: Res<GameSettings>,
mut commands: Commands,
mut gizmos: Gizmos,
) {
for (ent, mut state, mut translation, mut prev_translation, mut input, mut velocity, mut ecb, mut prev_ecb, attribs) in query {
let mut state_context = FighterStateContext {
@@ -183,7 +182,8 @@ pub fn state_collision_interrupt_system(
fighter_attribs: &attribs,
game_settings: &game_settings,
entity: ent,
stage_collision: &stage_collision
stage_collision: &stage_collision,
gizmos: Some(&mut gizmos),
};
if let Some(new_state) = state.check_collision_interrupt(&mut state_context) {
let mut new_state = new_state;
@@ -197,4 +197,4 @@ pub fn state_collision_interrupt_system(
info!("State {} -> {}", old_state_name, new_state_name);
}
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
use bevy::prelude::*;
use super::{FighterStateImpl, FighterStateContext, FighterStateTransition, ground, wait, walk};
use super::{FighterStateImpl, FighterStateContext, ground, wait, walk};
use crate::fighter::state::FighterState;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{GroundedMotionResult, GroundedStateCommon};
+5 -2
View File
@@ -1,6 +1,6 @@
use bevy::prelude::*;
use super::{FighterStateImpl, FighterStateContext, FighterStateTransition, ground, walk};
use super::{FighterStateImpl, FighterStateContext, ground, walk};
use crate::fighter::state::FighterState;
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{GroundedMotionResult, GroundedStateCommon};
@@ -33,7 +33,10 @@ impl FighterStateImpl for WaitState {
} else {
state_context.fighter_attribs.ground_friction
};
state_context.velocity.x += ground::apply_grounded_friction(friction, state_context.velocity.x);
let ground_velocity = state_context.velocity.x;
state_context.velocity.x += ground::apply_grounded_friction(friction, ground_velocity);
ground::apply_grounded_motion(
state_context.velocity,
+1 -1
View File
@@ -1,6 +1,6 @@
use bevy::prelude::*;
use super::{FighterStateImpl, FighterStateContext, FighterStateTransition, dash, ground, wait};
use super::{FighterStateImpl, FighterStateContext, dash, ground, wait};
use crate::fighter::state::fall::FallState;
use crate::fighter::state::ground::{GroundedMotionResult, GroundedStateCommon};
use crate::fighter::state::{self, FighterState};