Skip to content

Commit

Permalink
Add directional knockback
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrankel committed Jul 23, 2024
1 parent 33f337c commit f07121b
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 26 deletions.
33 changes: 11 additions & 22 deletions src/game/actor/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,52 +18,41 @@ pub(super) fn plugin(app: &mut App) {
#[derive(Component, Reflect, Serialize, Deserialize, Copy, Clone)]
#[reflect(Component)]
pub struct Movement {
/// The acceleration when controller is active (pixels per second^2).
/// The acceleration rate (pixels per second^2).
/// Applies when the controller is active and speed is under control.
pub accel: f32,
/// The deceleration factor when controller is idle (decay per second).
/// The deceleration factor (multiplier per second).
/// Applies when the controller is inactive or speed is out of control.
pub decel: f32,
/// The maximum speed (pixels per second).
/// The max "under control" speed (pixels per second).
pub speed: f32,
}

impl Configure for Movement {
fn configure(app: &mut App) {
app.register_type::<Self>();
app.add_systems(
Update,
(
apply_movement.in_set(UpdateSet::Update),
clamp_movement_speed.in_set(UpdateSet::SyncLate),
),
);
app.add_systems(Update, apply_movement.in_set(UpdateSet::Update));
}
}

const EPSILON: f32 = 0.01;

fn apply_movement(
time: Res<Time>,
mut movement_query: Query<(&Movement, &MovementController, &mut LinearVelocity)>,
) {
let dt = time.delta_seconds();

for (movement, controller, mut velocity) in &mut movement_query {
if controller.0 != Vec2::ZERO {
velocity.0 += movement.accel * controller.0 * dt;
} else if velocity.0.length_squared() > EPSILON {
if controller.0 == Vec2::ZERO || velocity.0.length_squared() >= movement.speed.powi(2) {
// Apply deceleration.
velocity.0 *= movement.decel.powf(dt);
} else {
velocity.0 = Vec2::ZERO;
// Apply acceleration.
velocity.0 += movement.accel * controller.0 * dt;
velocity.0 = velocity.0.clamp_length_max(movement.speed);
}
}
}

fn clamp_movement_speed(mut movement_query: Query<(&Movement, &mut LinearVelocity)>) {
for (movement, mut velocity) in &mut movement_query {
velocity.0 = velocity.0.clamp_length_max(movement.speed);
}
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
pub struct MovementController(pub Vec2);
Expand Down
1 change: 1 addition & 0 deletions src/game/actor/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub fn player(entity: EntityWorldMut) {
// TODO: This is for testing hit effects until we get actual projectiles / attacks.
crate::game::combat::hit::Hitbox,
crate::game::combat::damage::HitboxDamage(2.0),
crate::game::combat::knockback::HitboxKnockback { force: 150.0 },
))
.with_children(|children| {
children
Expand Down
9 changes: 7 additions & 2 deletions src/game/combat.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
pub mod damage;
pub mod death;
pub mod hit;
// TODO: pub mod knockback
pub mod knockback;

use bevy::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.add_plugins((hit::plugin, damage::plugin, death::plugin));
app.add_plugins((
damage::plugin,
death::plugin,
hit::plugin,
knockback::plugin,
));
}
4 changes: 2 additions & 2 deletions src/game/combat/damage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ impl Configure for HitboxDamage {
fn apply_hitbox_damage(
trigger: Trigger<OnHit>,
mut commands: Commands,
damage_query: Query<&HitboxDamage>,
hitbox_query: Query<&HitboxDamage>,
) {
let &OnHit(hitbox, hurtbox) = trigger.event();
let damage = r!(damage_query.get(hitbox));
let damage = r!(hitbox_query.get(hitbox));
commands.entity(hurtbox).trigger(OnDamage(damage.0));
}
1 change: 1 addition & 0 deletions src/game/combat/death.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(super) fn plugin(app: &mut App) {
}

/// An observable event on an actor's death.
/// Remember to filter out `IsDead` entities before triggering this event.
#[derive(Event)]
pub struct OnDeath;

Expand Down
37 changes: 37 additions & 0 deletions src/game/combat/knockback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use avian2d::prelude::*;
use bevy::prelude::*;

use crate::game::combat::hit::OnHit;
use crate::util::prelude::*;

pub(super) fn plugin(app: &mut App) {
app.configure::<HitboxKnockback>();
}

#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct HitboxKnockback {
pub force: f32,
}

impl Configure for HitboxKnockback {
fn configure(app: &mut App) {
app.register_type::<Self>();
app.observe(apply_hitbox_knockback);
}
}

fn apply_hitbox_knockback(
trigger: Trigger<OnHit>,
hitbox_query: Query<(&GlobalTransform, &HitboxKnockback)>,
mut hurtbox_query: Query<(&GlobalTransform, &mut LinearVelocity)>,
) {
let &OnHit(hitbox, hurtbox) = trigger.event();
let (hitbox_gt, knockback) = r!(hitbox_query.get(hitbox));
let (hurtbox_gt, mut velocity) = r!(hurtbox_query.get_mut(hurtbox));

let hitbox_pos = hitbox_gt.translation().xy();
let hurtbox_pos = hurtbox_gt.translation().xy();
let direction = Dir2::new(hurtbox_pos - hitbox_pos).unwrap_or(Dir2::EAST);
velocity.0 += knockback.force * direction;
}

0 comments on commit f07121b

Please sign in to comment.