Refactor state system
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@ rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold", "-Zshare-generics=y", ]
|
|||||||
codegen-backend = true
|
codegen-backend = true
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-backend = "cranelift"
|
codegen-backend = "llvm"
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
codegen-backend = "llvm"
|
codegen-backend = "llvm"
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
color::palettes::css::{GREEN, HOT_PINK, RED, YELLOW},
|
color::palettes::css::{HOT_PINK, YELLOW},
|
||||||
ecs::relationship::Relationship,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
pub mod plugin;
|
||||||
game_settings::GameSettings,
|
pub mod data;
|
||||||
input::player::{ControlledBy, FighterInput},
|
pub mod state;
|
||||||
stage::StagePlane,
|
pub mod states;
|
||||||
};
|
pub use plugin::*;
|
||||||
|
pub use data::*;
|
||||||
|
|
||||||
|
use crate::stage::StagePlane;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Grounded;
|
pub struct Grounded;
|
||||||
@@ -48,12 +50,9 @@ pub fn debug_draw_ecb(ecbs: Query<(&FighterECB, &FighterTranslation)>, mut gizmo
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn integrate_gravity(
|
pub fn integrate_gravity(
|
||||||
fighters: Query<(&mut FighterVelocity)>,
|
_fighters: Query<&mut FighterVelocity>,
|
||||||
time: Res<Time<Fixed>>,
|
_time: Res<Time<Fixed>>,
|
||||||
) {
|
) {
|
||||||
/*for (movement, mut velocity) in fighters {
|
|
||||||
//velocity.y -= movement.gravity;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_motion(
|
pub fn apply_motion(
|
||||||
@@ -62,7 +61,7 @@ pub fn apply_motion(
|
|||||||
&mut FighterTranslation,
|
&mut FighterTranslation,
|
||||||
&mut FighterPreviousTranslation,
|
&mut FighterPreviousTranslation,
|
||||||
)>,
|
)>,
|
||||||
time: Res<Time<Fixed>>,
|
_time: Res<Time<Fixed>>,
|
||||||
) {
|
) {
|
||||||
for (velocity, mut translation, mut prev_translation) in fighters {
|
for (velocity, mut translation, mut prev_translation) in fighters {
|
||||||
prev_translation.0 = translation.0;
|
prev_translation.0 = translation.0;
|
||||||
@@ -98,3 +97,4 @@ pub fn collide_fighter_with_scene(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fighter_movement() {}
|
pub fn fighter_movement() {}
|
||||||
|
|
||||||
+7
-1
@@ -5,6 +5,12 @@ pub struct FighterAttributes {
|
|||||||
pub base_walk_accel: f32,
|
pub base_walk_accel: f32,
|
||||||
pub stick_walk_accel: f32,
|
pub stick_walk_accel: f32,
|
||||||
pub max_walk_vel: f32,
|
pub max_walk_vel: f32,
|
||||||
|
|
||||||
|
pub dash_duration: u32,
|
||||||
|
pub dash_initial_velocity: f32,
|
||||||
|
pub base_dash_accel: f32,
|
||||||
|
pub stick_dash_accel: f32,
|
||||||
|
pub max_dash_vel: f32,
|
||||||
|
|
||||||
pub ground_friction: f32,
|
pub ground_friction: f32,
|
||||||
pub dash_duration: u32
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
pub mod plugin;
|
|
||||||
pub mod fighter;
|
|
||||||
pub mod data;
|
|
||||||
pub mod state;
|
|
||||||
pub mod states;
|
|
||||||
pub use fighter::*;
|
|
||||||
pub use plugin::*;
|
|
||||||
pub use data::*;
|
|
||||||
+22
-3
@@ -1,8 +1,11 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use crate::fighter::states;
|
use bevy_egui::{EguiContexts, EguiPrimaryContextPass, egui};
|
||||||
|
use crate::fighter::state::StateNameDebug;
|
||||||
|
use crate::fighter::{FighterVelocity, states};
|
||||||
use crate::fighter::{
|
use crate::fighter::{
|
||||||
FighterECB, FighterTranslation, FighterVelocity, apply_motion, collide_fighter_with_scene, integrate_gravity, state::state_interrupt_system,
|
apply_motion, collide_fighter_with_scene, integrate_gravity, state::state_interrupt_system,
|
||||||
};
|
};
|
||||||
|
use crate::input::player::FighterInput;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FighterPlugin;
|
pub struct FighterPlugin;
|
||||||
@@ -16,14 +19,30 @@ impl Plugin for FighterPlugin {
|
|||||||
state_interrupt_system::<states::wait::WaitState>,
|
state_interrupt_system::<states::wait::WaitState>,
|
||||||
state_interrupt_system::<states::walk::WalkState>,
|
state_interrupt_system::<states::walk::WalkState>,
|
||||||
state_interrupt_system::<states::dash::DashState>,
|
state_interrupt_system::<states::dash::DashState>,
|
||||||
|
state_interrupt_system::<states::run::RunState>,
|
||||||
|
states::wait::wait_update,
|
||||||
states::walk::walk_update,
|
states::walk::walk_update,
|
||||||
states::dash::dash_update,
|
states::dash::dash_update,
|
||||||
|
states::run::run_update,
|
||||||
integrate_gravity,
|
integrate_gravity,
|
||||||
apply_motion,
|
apply_motion,
|
||||||
collide_fighter_with_scene,
|
collide_fighter_with_scene,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
)
|
)
|
||||||
.add_observer(states::dash::on_insert_dash);
|
.add_observer(states::dash::on_insert_dash)
|
||||||
|
.add_systems(EguiPrimaryContextPass, fighter_debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fighter_debug(mut contexts: EguiContexts, query: Query<(&FighterVelocity, &FighterInput, &StateNameDebug)>) -> Result {
|
||||||
|
for (vel, input, name) in query {
|
||||||
|
egui::Window::new("Hello").show(contexts.ctx_mut()?, |ui| {
|
||||||
|
ui.label(format!("State: {}", name.0));
|
||||||
|
ui.label(format!("{:?}", vel));
|
||||||
|
ui.label(format!("{:?}", input.get_last_frame()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
+23
-2
@@ -1,11 +1,12 @@
|
|||||||
use bevy::{ecs::relationship::RelationshipSourceCollection, prelude::*};
|
use bevy::{prelude::*};
|
||||||
|
|
||||||
use crate::{fighter::{FighterAttributes, states}, game_settings::GameSettings, input::player::FighterInput};
|
use crate::{fighter::{FighterAttributes, states}, game_settings::GameSettings, input::player::FighterInput};
|
||||||
|
|
||||||
pub enum FighterStateTransition {
|
pub enum FighterStateTransition {
|
||||||
Wait,
|
Wait,
|
||||||
Walk,
|
Walk,
|
||||||
Dash
|
Dash,
|
||||||
|
Run,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FighterState : Component + Sized {
|
pub trait FighterState : Component + Sized {
|
||||||
@@ -33,9 +34,28 @@ fn create_state(transition: FighterStateTransition, state_context: FighterStateC
|
|||||||
FighterStateTransition::Wait => ent_cmd.insert(states::wait::WaitState),
|
FighterStateTransition::Wait => ent_cmd.insert(states::wait::WaitState),
|
||||||
FighterStateTransition::Walk => ent_cmd.insert(states::walk::WalkState),
|
FighterStateTransition::Walk => ent_cmd.insert(states::walk::WalkState),
|
||||||
FighterStateTransition::Dash => ent_cmd.insert(states::dash::DashState::default()),
|
FighterStateTransition::Dash => ent_cmd.insert(states::dash::DashState::default()),
|
||||||
|
FighterStateTransition::Run => ent_cmd.insert(states::run::RunState),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_state_debug_name(transition: &FighterStateTransition) -> &'static str {
|
||||||
|
match transition {
|
||||||
|
FighterStateTransition::Wait => "wait",
|
||||||
|
FighterStateTransition::Walk => "walk",
|
||||||
|
FighterStateTransition::Dash => "dash",
|
||||||
|
FighterStateTransition::Run => "run",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct StateNameDebug(pub &'static str);
|
||||||
|
|
||||||
|
impl std::fmt::Display for StateNameDebug {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn state_interrupt_system<T: FighterState + Component + std::fmt::Debug>(
|
pub fn state_interrupt_system<T: FighterState + Component + std::fmt::Debug>(
|
||||||
query: Query<(Entity, &T, &mut FighterInput, &FighterAttributes)>,
|
query: Query<(Entity, &T, &mut FighterInput, &FighterAttributes)>,
|
||||||
game_settings: Res<GameSettings>,
|
game_settings: Res<GameSettings>,
|
||||||
@@ -51,6 +71,7 @@ pub fn state_interrupt_system<T: FighterState + Component + std::fmt::Debug>(
|
|||||||
};
|
};
|
||||||
if let Some(new_state) = state.check_interrupt(&state_context) {
|
if let Some(new_state) = state.check_interrupt(&state_context) {
|
||||||
commands.entity(ent).remove::<T>();
|
commands.entity(ent).remove::<T>();
|
||||||
|
commands.entity(ent).insert(StateNameDebug(get_state_debug_name(&new_state)));
|
||||||
create_state(new_state, state_context, &mut commands);
|
create_state(new_state, state_context, &mut commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod wait;
|
||||||
|
pub mod walk;
|
||||||
|
pub mod dash;
|
||||||
|
pub mod run;
|
||||||
|
pub mod grounded;
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
fighter::state::{FighterState, FighterStateContext, FighterStateTransition},
|
fighter::{
|
||||||
|
FighterAttributes, FighterVelocity,
|
||||||
|
state::{FighterState, FighterStateContext, FighterStateTransition},
|
||||||
|
states::grounded,
|
||||||
|
},
|
||||||
|
game_settings::GameSettings,
|
||||||
input::player::FighterInput,
|
input::player::FighterInput,
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -24,7 +29,8 @@ impl FighterState for DashState {
|
|||||||
) -> Option<FighterStateTransition> {
|
) -> Option<FighterStateTransition> {
|
||||||
if self.frames_in_dash > state_context.fighter_attribs.dash_duration {
|
if self.frames_in_dash > state_context.fighter_attribs.dash_duration {
|
||||||
// Dash finish shenanigans
|
// Dash finish shenanigans
|
||||||
super::walk::check_input(state_context)
|
super::run::check_input(state_context)
|
||||||
|
.or_else(|| super::walk::check_input(state_context))
|
||||||
.or_else(|| super::wait::check_input(state_context))
|
.or_else(|| super::wait::check_input(state_context))
|
||||||
} else if let Some(last_frame) = state_context.input.get_last_frame() {
|
} else if let Some(last_frame) = state_context.input.get_last_frame() {
|
||||||
check_smash_input_with_dir(state_context)
|
check_smash_input_with_dir(state_context)
|
||||||
@@ -57,9 +63,29 @@ fn check_smash_input_with_dir(state_context: &FighterStateContext) -> Option<f32
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dash_update(dash_query: Query<&mut DashState>) {
|
pub fn dash_update(
|
||||||
for mut dash in dash_query {
|
dash_query: Query<(
|
||||||
|
&mut DashState,
|
||||||
|
&FighterAttributes,
|
||||||
|
&FighterInput,
|
||||||
|
&mut FighterVelocity,
|
||||||
|
)>,
|
||||||
|
game_settings: Res<GameSettings>,
|
||||||
|
) {
|
||||||
|
for (mut dash, attributes, input, mut velocity) in dash_query {
|
||||||
dash.frames_in_dash += 1;
|
dash.frames_in_dash += 1;
|
||||||
|
|
||||||
|
if let Some(last_frame) = input.get_last_frame() {
|
||||||
|
let (accel, target_vel) = attributes.get_accel_and_target_dashrun(last_frame);
|
||||||
|
let accel = grounded::compute_ground_accel(
|
||||||
|
accel,
|
||||||
|
target_vel,
|
||||||
|
velocity.x,
|
||||||
|
attributes,
|
||||||
|
game_settings.as_ref(),
|
||||||
|
);
|
||||||
|
velocity.x += accel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,10 +95,21 @@ pub fn check_input(state_context: &FighterStateContext) -> Option<FighterStateTr
|
|||||||
|
|
||||||
pub fn on_insert_dash(
|
pub fn on_insert_dash(
|
||||||
add: On<Insert, DashState>,
|
add: On<Insert, DashState>,
|
||||||
query: Query<(Entity, &mut FighterInput), With<DashState>>,
|
query: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&mut FighterInput,
|
||||||
|
&FighterAttributes,
|
||||||
|
&mut FighterVelocity,
|
||||||
|
),
|
||||||
|
With<DashState>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut input) in query {
|
for (entity, mut input, attribs, mut velocity) in query {
|
||||||
if entity == add.entity {
|
if entity == add.entity {
|
||||||
|
if let Some(last_frame) = input.get_last_frame() {
|
||||||
|
velocity.x = attribs.dash_initial_velocity * last_frame.movement.x.signum();
|
||||||
|
}
|
||||||
input.clear_buffer();
|
input.clear_buffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
use bevy::log::info;
|
||||||
|
|
||||||
|
use crate::{fighter::FighterAttributes, game_settings::GameSettings, input::player::InputFrame};
|
||||||
|
|
||||||
|
pub fn apply_grounded_friction(friction: f32, ground_vel: f32) -> f32 {
|
||||||
|
if friction.abs() > ground_vel.abs() {
|
||||||
|
-ground_vel
|
||||||
|
} else {
|
||||||
|
(-ground_vel.signum()) * friction.abs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_accelerate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_ground_accel(accel: f32, target_vel: f32, gr_vel: f32, attribs: &FighterAttributes, game_settings: &GameSettings) -> f32 {
|
||||||
|
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) {
|
||||||
|
// accelerating, not reversing
|
||||||
|
if accel > 0.0 {
|
||||||
|
if gr_vel + accel > target_vel {
|
||||||
|
accel = -attribs.ground_friction;
|
||||||
|
if gr_vel + accel < target_vel {
|
||||||
|
accel = target_vel - gr_vel;
|
||||||
|
}
|
||||||
|
if gr_vel + accel > ground_max_horizontal_velocity {
|
||||||
|
accel = ground_max_horizontal_velocity - gr_vel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if gr_vel + accel < target_vel {
|
||||||
|
accel = attribs.ground_friction;
|
||||||
|
if gr_vel + accel > target_vel {
|
||||||
|
accel = target_vel - gr_vel;
|
||||||
|
}
|
||||||
|
if gr_vel + accel < -ground_max_horizontal_velocity {
|
||||||
|
accel = -ground_max_horizontal_velocity - gr_vel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FighterAttributes {
|
||||||
|
pub fn get_accel_and_target_dashrun(&self, input: &InputFrame) -> (f32, f32) {
|
||||||
|
let accel = input.movement.x * self.stick_dash_accel;
|
||||||
|
let accel = accel + input.movement.x.signum() * self.base_dash_accel;
|
||||||
|
let target_vel = input.movement.x * self.max_dash_vel;
|
||||||
|
|
||||||
|
info!("accel {:?} target_vel {:?}", accel, target_vel);
|
||||||
|
|
||||||
|
(accel, target_vel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_accel_and_target_walk(&self, input: &InputFrame) -> (f32, f32) {
|
||||||
|
let accel = input.movement.x * self.stick_walk_accel;
|
||||||
|
let accel = accel + input.movement.x.signum() * self.base_walk_accel;
|
||||||
|
let target_vel = input.movement.x * self.max_walk_vel;
|
||||||
|
|
||||||
|
(accel, target_vel)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod wait;
|
|
||||||
pub mod walk;
|
|
||||||
pub mod dash;
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
use crate::{
|
||||||
|
fighter::{FighterAttributes, FighterVelocity, state::{FighterStateContext, FighterStateTransition}, states::grounded}, game_settings::GameSettings, input::player::FighterInput, state::FighterState,
|
||||||
|
};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
pub struct RunState;
|
||||||
|
|
||||||
|
pub fn check_input(state_context: &FighterStateContext) -> Option<FighterStateTransition> {
|
||||||
|
if let Some(input_frame) = state_context.input.get_last_frame() {
|
||||||
|
if input_frame.movement.x.abs() >= state_context.game_settings.input_common.run_stick_threshold {
|
||||||
|
return Some(FighterStateTransition::Run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FighterState for RunState {
|
||||||
|
fn check_interrupt(
|
||||||
|
&self,
|
||||||
|
state_context: &crate::fighter::state::FighterStateContext,
|
||||||
|
) -> Option<crate::fighter::state::FighterStateTransition> {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
if let Some(_) = check_input(state_context) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
walk::check_input(state_context).or_else(|| wait::check_input(state_context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_update(
|
||||||
|
fighters: Query<(&mut FighterVelocity, &FighterAttributes, &FighterInput), With<RunState>>,
|
||||||
|
game_settings: Res<GameSettings>,
|
||||||
|
) {
|
||||||
|
for ( mut velocity, attributes, input) in fighters {
|
||||||
|
if let Some(last_frame) = input.get_last_frame() {
|
||||||
|
let (accel, target_vel) = attributes.get_accel_and_target_dashrun(last_frame);
|
||||||
|
let accel = grounded::compute_ground_accel(accel, target_vel, velocity.x, attributes, game_settings.as_ref());
|
||||||
|
velocity.x += accel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use bevy::prelude::*;
|
use crate::{fighter::{state::{FighterState, FighterStateContext, FighterStateTransition}, states::{grounded, walk}}, game_settings::GameSettings};
|
||||||
use crate::fighter::{state::{FighterState, FighterStateContext, FighterStateTransition}, states::walk};
|
use crate::fighter::*;
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
pub struct WaitState;
|
pub struct WaitState;
|
||||||
@@ -21,3 +21,14 @@ pub fn check_input(state_context: &FighterStateContext) -> Option<FighterStateTr
|
|||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wait_update(query: Query<(&FighterAttributes, &mut FighterVelocity), With<WaitState>>, game_settings: Res<GameSettings>) {
|
||||||
|
for (attribs, mut velocity) in query {
|
||||||
|
let friction = if velocity.x.abs() > attribs.max_walk_vel {
|
||||||
|
attribs.ground_friction * game_settings.figher_common.ground_friction_over_walk_speed_multiplier
|
||||||
|
} else {
|
||||||
|
attribs.ground_friction
|
||||||
|
};
|
||||||
|
velocity.x += grounded::apply_grounded_friction(friction, velocity.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
fighter::{
|
fighter::{
|
||||||
FighterAttributes, FighterVelocity,
|
FighterAttributes, FighterVelocity, state::{FighterState, FighterStateContext, FighterStateTransition}, states::{dash, grounded, wait},
|
||||||
state::{FighterState, FighterStateContext, FighterStateTransition},
|
}, game_settings::GameSettings, input::player::FighterInput,
|
||||||
states::{dash, wait},
|
|
||||||
},
|
|
||||||
game_settings::GameSettings,
|
|
||||||
input::player::FighterInput,
|
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
@@ -31,53 +27,9 @@ pub fn walk_update(
|
|||||||
) {
|
) {
|
||||||
for (mut velocity, attributes, input) in fighters {
|
for (mut velocity, attributes, input) in fighters {
|
||||||
if let Some(last_frame) = input.get_last_frame() {
|
if let Some(last_frame) = input.get_last_frame() {
|
||||||
let accel_mul = 1.0;
|
let (accel, target_vel) = attributes.get_accel_and_target_walk(last_frame);
|
||||||
let mut accel = last_frame.movement.x * attributes.stick_walk_accel * accel_mul; // stick-proportional
|
|
||||||
accel += (last_frame.movement.x.signum() * attributes.base_walk_accel) * accel_mul; // constant
|
|
||||||
let target_vel = last_frame.movement.x * attributes.max_walk_vel * accel_mul;
|
|
||||||
|
|
||||||
if target_vel != 0.0 {
|
let accel = grounded::compute_ground_accel(accel, target_vel, velocity.x, attributes, &game_settings);
|
||||||
let mult = velocity.x / target_vel;
|
|
||||||
if mult > 0.0 && mult < 1.0 {
|
|
||||||
accel *= (1.0 - mult) * game_settings.figher_common.walk_speed_ease; // 0.5 — linear ease-out toward target
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Friction mode
|
|
||||||
if target_vel == 0.0 {
|
|
||||||
let mut out_friction = velocity.x.signum() * (-attributes.ground_friction);
|
|
||||||
// apply friction
|
|
||||||
if attributes.ground_friction.abs() > velocity.x.abs() {
|
|
||||||
out_friction = -velocity.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
accel = out_friction;
|
|
||||||
} else {
|
|
||||||
let ground_max_horizontal_velocity =
|
|
||||||
game_settings.figher_common.ground_max_horizontal_velocity;
|
|
||||||
if !(velocity.x * accel < 0.0) {
|
|
||||||
// accelerating, not reversing
|
|
||||||
if accel > 0.0 {
|
|
||||||
if velocity.x + accel > target_vel {
|
|
||||||
accel = -attributes.ground_friction;
|
|
||||||
if velocity.x + accel < target_vel {
|
|
||||||
accel = target_vel - velocity.x;
|
|
||||||
}
|
|
||||||
if velocity.x + accel > ground_max_horizontal_velocity {
|
|
||||||
accel = ground_max_horizontal_velocity - velocity.x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if velocity.x + accel < target_vel {
|
|
||||||
accel = attributes.ground_friction;
|
|
||||||
if velocity.x + accel > target_vel {
|
|
||||||
accel = target_vel - velocity.x;
|
|
||||||
}
|
|
||||||
if velocity.x + accel < -ground_max_horizontal_velocity {
|
|
||||||
accel = -ground_max_horizontal_velocity - velocity.x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
velocity.x += accel;
|
velocity.x += accel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,15 @@ use bevy::prelude::*;
|
|||||||
pub struct FighterSettingsCommon {
|
pub struct FighterSettingsCommon {
|
||||||
pub walk_speed_ease: f32,
|
pub walk_speed_ease: f32,
|
||||||
pub ground_max_horizontal_velocity: f32,
|
pub ground_max_horizontal_velocity: f32,
|
||||||
|
pub ground_friction_over_walk_speed_multiplier: f32
|
||||||
}
|
}
|
||||||
pub struct InputSettingsCommon {
|
pub struct InputSettingsCommon {
|
||||||
pub stick_deadzone: f32,
|
pub stick_deadzone: f32,
|
||||||
pub trigger_deadzone: f32,
|
pub trigger_deadzone: f32,
|
||||||
pub smash_input_reset_axis_threshold: f32,
|
pub smash_input_reset_axis_threshold: f32,
|
||||||
pub smash_input_axis_threshold: f32,
|
pub smash_input_axis_threshold: f32,
|
||||||
pub smash_input_frame_threshold: u32
|
pub run_stick_threshold: f32,
|
||||||
|
pub smash_input_frame_threshold: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InputSettingsCommon {
|
impl Default for InputSettingsCommon {
|
||||||
@@ -19,6 +21,7 @@ impl Default for InputSettingsCommon {
|
|||||||
trigger_deadzone: 0.3f32,
|
trigger_deadzone: 0.3f32,
|
||||||
smash_input_reset_axis_threshold: 0.25,
|
smash_input_reset_axis_threshold: 0.25,
|
||||||
smash_input_axis_threshold: 0.8,
|
smash_input_axis_threshold: 0.8,
|
||||||
|
run_stick_threshold: 0.625,
|
||||||
smash_input_frame_threshold: 2
|
smash_input_frame_threshold: 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ use crate::input::player::InputFrame;
|
|||||||
use super::player::{FighterInput, BaseInputMap, InputActionState};
|
use super::player::{FighterInput, BaseInputMap, InputActionState};
|
||||||
|
|
||||||
pub fn postprocess_input(input_state: Query<&mut FighterInput>) {
|
pub fn postprocess_input(input_state: Query<&mut FighterInput>) {
|
||||||
for mut player_input in input_state {
|
for _player_input in input_state {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn preprocess_keyboard_input(key: Res<ButtonInput<KeyCode>>, input_map: Res<BaseInputMap>, mut input_state: Query<&mut FighterInput>) {
|
pub fn preprocess_keyboard_input(key: Res<ButtonInput<KeyCode>>, input_map: Res<BaseInputMap>, mut input_state: Query<&mut FighterInput>) {
|
||||||
|
|||||||
+10
-4
@@ -1,23 +1,28 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use arrayvec::ArrayVec;
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct InputFrame {
|
pub struct InputFrame {
|
||||||
pub movement: Vec2,
|
pub movement: Vec2,
|
||||||
pub directional_attack: Vec2
|
pub directional_attack: Vec2
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Default)]
|
#[derive(Component)]
|
||||||
pub struct FighterInput {
|
pub struct FighterInput {
|
||||||
input_buffer: VecDeque<InputFrame>
|
input_buffer: VecDeque<InputFrame>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for FighterInput {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { input_buffer: vec!(InputFrame::default()).into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FighterInput {
|
impl FighterInput {
|
||||||
const INPUT_BUFFER_SIZE: usize = 5;
|
const INPUT_BUFFER_SIZE: usize = 5;
|
||||||
pub fn get_last_frame(&self) -> Option<&InputFrame> {
|
pub fn get_last_frame(&self) -> Option<&InputFrame> {
|
||||||
self.input_buffer.back()
|
Some(self.input_buffer.back().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_input_frame(&mut self, frame: InputFrame) {
|
pub fn push_input_frame(&mut self, frame: InputFrame) {
|
||||||
@@ -28,6 +33,7 @@ impl FighterInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_buffer(&mut self) {
|
pub fn clear_buffer(&mut self) {
|
||||||
|
info!("CLEAR");
|
||||||
while self.input_buffer.len() > 1 {
|
while self.input_buffer.len() > 1 {
|
||||||
self.input_buffer.pop_front();
|
self.input_buffer.pop_front();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use crate::fighter::*;
|
|
||||||
use crate::input::player::{ControlledBy, Controls, FighterInput};
|
|
||||||
use crate::input::{
|
use crate::input::{
|
||||||
logic::{postprocess_input, preprocess_keyboard_input}, player::{BaseInputMap, InputMapAction, KeyboardInputMapElement},
|
logic::{postprocess_input, preprocess_keyboard_input}, player::{BaseInputMap, InputMapAction, KeyboardInputMapElement},
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-3
@@ -38,7 +38,7 @@ pub mod game_settings;
|
|||||||
pub struct GamePlugin;
|
pub struct GamePlugin;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
let fighter = commands
|
let _fighter = commands
|
||||||
.spawn((
|
.spawn((
|
||||||
Name("Fighter".into()),
|
Name("Fighter".into()),
|
||||||
FighterECB {
|
FighterECB {
|
||||||
@@ -52,7 +52,11 @@ pub fn setup(mut commands: Commands) {
|
|||||||
stick_walk_accel: 0.02,
|
stick_walk_accel: 0.02,
|
||||||
max_walk_vel: 0.16,
|
max_walk_vel: 0.16,
|
||||||
ground_friction: 0.008,
|
ground_friction: 0.008,
|
||||||
dash_duration: 15
|
dash_duration: 15,
|
||||||
|
base_dash_accel: 0.002,
|
||||||
|
stick_dash_accel: 0.01,
|
||||||
|
max_dash_vel: 0.22,
|
||||||
|
dash_initial_velocity: 0.19,
|
||||||
},
|
},
|
||||||
WaitState {},
|
WaitState {},
|
||||||
input::player::FighterInput::default()
|
input::player::FighterInput::default()
|
||||||
@@ -71,7 +75,8 @@ impl Plugin for GamePlugin {
|
|||||||
.insert_resource(GameSettings {
|
.insert_resource(GameSettings {
|
||||||
figher_common: FighterSettingsCommon {
|
figher_common: FighterSettingsCommon {
|
||||||
walk_speed_ease: 0.5,
|
walk_speed_ease: 0.5,
|
||||||
ground_max_horizontal_velocity: 0.3
|
ground_max_horizontal_velocity: 0.3,
|
||||||
|
ground_friction_over_walk_speed_multiplier: 2.0
|
||||||
},
|
},
|
||||||
input_common: InputSettingsCommon::default(),
|
input_common: InputSettingsCommon::default(),
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
use bevy::{camera_controller::free_camera::FreeCamera, color::palettes::css::{GREEN, RED}, prelude::*};
|
use bevy::{color::palettes::css::{GREEN, RED}, prelude::*};
|
||||||
|
|
||||||
#[derive(Component, Reflect, Clone, Default)]
|
#[derive(Component, Reflect, Clone, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
|||||||
Reference in New Issue
Block a user