Implement tap jump and clean some fluff (#83)
CI / build-and-test (push) Successful in 1m33s
CI / production-build (push) Successful in 11m45s

Reviewed-on: #83
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #83.
This commit is contained in:
2026-10-05 15:01:31 +00:00
committed by EIREXE
parent d2d649b059
commit 5c277a00f0
5 changed files with 67 additions and 47 deletions
+13 -16
View File
@@ -1,6 +1,8 @@
use crate::fighter::state::jump::{JumpState, JumpType}; use crate::fighter::state::jump::{self};
use crate::fighter::state::land::{LandingState, LandingType}; use crate::fighter::state::land::{LandingState, LandingType};
use crate::fighter::state::{CollisionContext, FighterState, InterruptContext, air_attack, air_dodge}; use crate::fighter::state::{
CollisionContext, FighterState, InterruptContext, air_attack, air_dodge,
};
use crate::fighter::*; use crate::fighter::*;
use crate::input::FighterCommands; use crate::input::FighterCommands;
@@ -106,22 +108,17 @@ pub fn update_standard_motion(
airborne_common.is_fast_falling, airborne_common.is_fast_falling,
); );
apply_air_drift(ctx.velocity, ctx.fighter_attributes, ctx.input); apply_air_drift(ctx.velocity, ctx.fighter_attributes, ctx.input);
apply_air_motion( apply_air_motion(ctx.velocity, ctx.translation, ctx.prev_translation);
ctx.velocity,
ctx.translation,
ctx.prev_translation,
);
} }
pub fn air_interrupts_common(ctx: &mut InterruptContext, airborne_state_common: &AirborneStateCommon) -> Option<FighterState> { pub fn air_interrupts_common(
if ctx.input.has_command(FighterCommands::Jump) { ctx: &mut InterruptContext,
Some(FighterState::Jump(JumpState { airborne_state_common: &AirborneStateCommon,
jump_type: JumpType::DoubleJump, ) -> Option<FighterState> {
airborne_state_common: AirborneStateCommon::default(), jump::check_input_double_jump(ctx).or_else(|| {
})) air_dodge::check_input(ctx)
} else { .or_else(|| air_attack::check_interrupt(ctx, airborne_state_common))
air_dodge::check_input(ctx).or_else(|| air_attack::check_interrupt(ctx, airborne_state_common)) })
}
} }
pub fn collision_interrupt( pub fn collision_interrupt(
+46 -23
View File
@@ -1,4 +1,3 @@
use bevy::prelude::*;
use crate::{ use crate::{
fighter::{ fighter::{
@@ -21,21 +20,30 @@ pub enum JumpType {
DoubleJump, DoubleJump,
} }
#[derive(Debug, Clone, Hash)]
pub enum JumpStateInitiator {
Tap,
Button,
}
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct JumpState { pub struct JumpState {
pub jump_type: JumpType, pub jump_type: JumpType,
pub airborne_state_common: AirborneStateCommon, pub airborne_state_common: AirborneStateCommon,
pub initiator: JumpStateInitiator,
} }
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct JumpSquatState { pub struct JumpSquatState {
pub duration_counter: u32, pub duration_counter: u32,
pub initiator: JumpStateInitiator,
} }
impl JumpSquatState { impl JumpSquatState {
pub fn create() -> Self { pub fn create(initiator: JumpStateInitiator) -> Self {
Self { Self {
duration_counter: 0, duration_counter: 0,
initiator,
} }
} }
} }
@@ -68,17 +76,17 @@ impl FighterStateImpl for JumpSquatState {
direction: wavedash_dir.normalize_or_zero(), direction: wavedash_dir.normalize_or_zero(),
})) }))
} else if self.duration_counter >= state_context.fighter_attributes.jumpsquat_duration { } else if self.duration_counter >= state_context.fighter_attributes.jumpsquat_duration {
info!( let tap_jump_held = matches!(self.initiator, JumpStateInitiator::Tap)
"Jump {}", && state_context.input.get_last_frame().movement.y
state_context > state_context
.game_settings
.input_common
.smash_input_axis_threshold;
let jump_type = if tap_jump_held
|| state_context
.input .input
.get_last_frame() .get_last_frame()
.is_held(DigitalAction::Jump) .is_held(DigitalAction::Jump)
);
let jump_type = if state_context
.input
.get_last_frame()
.is_held(DigitalAction::Jump)
{ {
JumpType::LongJump JumpType::LongJump
} else { } else {
@@ -87,6 +95,7 @@ impl FighterStateImpl for JumpSquatState {
Some(FighterState::Jump(JumpState { Some(FighterState::Jump(JumpState {
jump_type, jump_type,
airborne_state_common: AirborneStateCommon::default(), airborne_state_common: AirborneStateCommon::default(),
initiator: self.initiator.clone(),
})) }))
} else { } else {
None None
@@ -109,6 +118,7 @@ impl FighterStateImpl for JumpSquatState {
state_context.play_animation(AnimKind::JumpSquat, false); state_context.play_animation(AnimKind::JumpSquat, false);
state_context.input.enable_hold_all(10); state_context.input.enable_hold_all(10);
state_context.input.clear_command(FighterCommands::Jump); state_context.input.clear_command(FighterCommands::Jump);
state_context.input.clear_command(FighterCommands::TapJump);
} }
fn on_exit(&mut self, state_context: &mut crate::fighter::state::ExitContext) { fn on_exit(&mut self, state_context: &mut crate::fighter::state::ExitContext) {
@@ -208,20 +218,33 @@ impl FighterStateImpl for JumpState {
} }
pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> { pub fn check_input(state_context: &InterruptContext) -> Option<FighterState> {
state_context if state_context.input.has_command(FighterCommands::Jump) {
.input Some(FighterState::JumpSquat(JumpSquatState::create(
.has_command(FighterCommands::Jump) JumpStateInitiator::Button,
.then(|| FighterState::JumpSquat(JumpSquatState::create())) )))
} else if state_context.input.has_command(FighterCommands::TapJump) {
Some(FighterState::JumpSquat(JumpSquatState::create(
JumpStateInitiator::Tap,
)))
} else {
None
}
} }
pub fn check_input_double_jump(state_context: &InterruptContext) -> Option<FighterState> { pub fn check_input_double_jump(state_context: &InterruptContext) -> Option<FighterState> {
state_context if state_context.input.has_command(FighterCommands::Jump) {
.input Some(FighterState::Jump(JumpState {
.has_command(FighterCommands::Jump) jump_type: JumpType::DoubleJump,
.then(|| { airborne_state_common: AirborneStateCommon::default(),
FighterState::Jump(JumpState { initiator: JumpStateInitiator::Button,
jump_type: JumpType::DoubleJump, }))
airborne_state_common: AirborneStateCommon::default(), } else if state_context.input.has_command(FighterCommands::TapJump) {
}) Some(FighterState::Jump(JumpState {
}) jump_type: JumpType::DoubleJump,
airborne_state_common: AirborneStateCommon::default(),
initiator: JumpStateInitiator::Tap,
}))
} else {
None
}
} }
+7 -1
View File
@@ -22,6 +22,7 @@ pub enum FighterCommands {
SmashMoveDown, SmashMoveDown,
SmashMoveLeft, SmashMoveLeft,
SmashMoveRight, SmashMoveRight,
TapJump,
} }
pub(crate) fn second_binding_pressed( pub(crate) fn second_binding_pressed(
@@ -32,7 +33,7 @@ pub(crate) fn second_binding_pressed(
} }
impl FighterCommands { impl FighterCommands {
pub const COUNT: usize = 8; pub const COUNT: usize = 9;
pub const fn index(self) -> usize { pub const fn index(self) -> usize {
match self { match self {
@@ -44,6 +45,7 @@ impl FighterCommands {
Self::SmashMoveDown => 5, Self::SmashMoveDown => 5,
Self::SmashMoveLeft => 6, Self::SmashMoveLeft => 6,
Self::SmashMoveRight => 7, Self::SmashMoveRight => 7,
Self::TapJump => 8,
} }
} }
} }
@@ -514,6 +516,10 @@ pub fn postprocess_input(
y_flick_detected = !x_flick_detected; y_flick_detected = !x_flick_detected;
} }
if y_flick_detected && input.current_frame.movement.y > 0.0 {
input.set_lifetime(FighterCommands::TapJump, tap);
}
if attack_pressed { if attack_pressed {
let attack_type = if y_flick_detected { let attack_type = if y_flick_detected {
if input.current_frame.movement.y > 0.0 { if input.current_frame.movement.y > 0.0 {
-2
View File
@@ -56,8 +56,6 @@ pub fn bind_fighter_material(
continue; continue;
}; };
dbg!(material_name);
if !material_name.starts_with("Fighter_") { if !material_name.starts_with("Fighter_") {
continue; continue;
} }
+1 -5
View File
@@ -8,16 +8,12 @@ pub mod fighter_material;
pub struct ShinesparkMaterialPlugin; pub struct ShinesparkMaterialPlugin;
pub fn startup_test(asset_server: Res<AssetServer>) {
let test: Handle<Shader> = asset_server.load("embedded://shinespark/shaders/fighter.wgsl");
dbg!(test);
}
impl Plugin for ShinesparkMaterialPlugin { impl Plugin for ShinesparkMaterialPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
let omit_prefix = ""; let omit_prefix = "";
embedded_asset!(app, omit_prefix, "shaders/fighter.wgsl"); embedded_asset!(app, omit_prefix, "shaders/fighter.wgsl");
app.add_systems(Startup, startup_test) app
.add_observer(fighter_material::bind_fighter_material) .add_observer(fighter_material::bind_fighter_material)
.add_plugins(MaterialPlugin::<fighter_material::FighterMaterial>::default()); .add_plugins(MaterialPlugin::<fighter_material::FighterMaterial>::default());
} }