Skip to content

Commit

Permalink
Implements physics based player movement
Browse files Browse the repository at this point in the history
  • Loading branch information
mnmaita committed Nov 18, 2023
1 parent 68d1d04 commit 505e150
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ pub use constants::*;
pub use enemy::Enemy;
pub use hitpoints::Hitpoints;
pub use level::{BorderTile, Tile};
pub use player::Player;
pub use player::{Player, PlayerMovementEvent};
pub use plugin::GamePlugin;
90 changes: 88 additions & 2 deletions src/game/player.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::Collider;
use bevy_rapier2d::prelude::*;

use crate::{
animation::{AnimationIndices, AnimationTimer},
physics::Speed,
AppState,
playing, AppState,
};

use super::Hitpoints;
Expand All @@ -13,7 +13,11 @@ pub(super) struct PlayerPlugin;

impl Plugin for PlayerPlugin {
fn build(&self, app: &mut App) {
app.add_event::<PlayerMovementEvent>();

app.add_systems(OnEnter(AppState::InGame), spawn_player);

app.add_systems(FixedUpdate, handle_player_movement_events.run_if(playing()));
}
}

Expand All @@ -22,8 +26,12 @@ pub struct PlayerBundle {
pub animation_indices: AnimationIndices,
pub animation_timer: AnimationTimer,
pub collider: Collider,
pub damping: Damping,
pub external_force: ExternalForce,
pub external_impulse: ExternalImpulse,
pub hitpoints: Hitpoints,
pub marker: Player,
pub rigid_body: RigidBody,
pub speed: Speed,
pub spritesheet: SpriteSheetBundle,
}
Expand All @@ -42,8 +50,12 @@ fn spawn_player(mut commands: Commands, asset_server: Res<AssetServer>) {
animation_indices: AnimationIndices::new(0, 2),
animation_timer: AnimationTimer::from_seconds(0.2),
collider: Collider::ball(80.5),
damping: Damping::default(),
external_force: ExternalForce::default(),
external_impulse: ExternalImpulse::default(),
hitpoints: Hitpoints::new(100),
marker: Player,
rigid_body: RigidBody::Dynamic,
speed: Speed(15.),
spritesheet: SpriteSheetBundle {
sprite: TextureAtlasSprite::new(0),
Expand All @@ -53,3 +65,77 @@ fn spawn_player(mut commands: Commands, asset_server: Res<AssetServer>) {
},
});
}

#[derive(Event)]
pub enum PlayerMovementEvent {
Accelerate { target: Vec2 },
Brake,
}

impl PlayerMovementEvent {
pub fn accelerate(target: Vec2) -> Self {
Self::Accelerate { target }
}

pub fn brake() -> Self {
Self::Brake
}
}

fn handle_player_movement_events(
mut player_movement_event_reader: EventReader<PlayerMovementEvent>,
mut query: Query<
(
&Transform,
&mut ExternalForce,
&mut ExternalImpulse,
&mut Damping,
),
With<Player>,
>,
) {
let (transform, mut external_force, mut external_impulse, mut damping) = query.single_mut();

for event in player_movement_event_reader.read() {
match event {
&PlayerMovementEvent::Accelerate { target } => {
let player_position = transform.translation.truncate();
let target_to_player_vector = target - player_position;

if target_to_player_vector == Vec2::ZERO {
continue;
}

let target_distance_to_player = target.distance(player_position);
let velocity_scalar = target_distance_to_player.min(300.) / 300.;
let direction = transform.rotation.mul_vec3(Vec3::Y).truncate();
let angle_with_cursor =
direction.angle_between(target_to_player_vector.normalize());
let is_in_cruise_mode = (-0.4..0.4).contains(&angle_with_cursor);
let angle = target_to_player_vector.angle_between(direction);

*damping = Damping::default();
external_impulse.impulse = direction * velocity_scalar * 1000.;

let (torque, strafe) = {
if is_in_cruise_mode {
external_impulse.impulse *= 2.;
damping.angular_damping = 10.;
(0., Vec2::ZERO)
} else {
(-angle * 250., direction.perp() * -angle * 125_000.)
}
};

external_impulse.torque_impulse = torque;
external_force.force = strafe;
}
PlayerMovementEvent::Brake => {
*damping = Damping {
angular_damping: 25.,
linear_damping: 25.,
};
}
}
}
}
31 changes: 5 additions & 26 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::f32::consts::FRAC_PI_2;

use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow};

use crate::{game::Player, playing};
use crate::{game::PlayerMovementEvent, playing};

pub struct InputPlugin;

Expand Down Expand Up @@ -35,32 +33,13 @@ impl CursorWorldPositionChecker<'_, '_> {
fn mouse_input(
mouse_input: ResMut<Input<MouseButton>>,
cursor_world_position_checker: CursorWorldPositionChecker,
mut query: Query<&mut Transform, With<Player>>,
mut player_movement_event_writer: EventWriter<PlayerMovementEvent>,
) {
if mouse_input.pressed(MouseButton::Right) {
if let Some(cursor_position) = cursor_world_position_checker.cursor_world_position() {
let mut player_transform = query.single_mut();
let player_position = player_transform.translation.truncate();
let cursor_to_player_vector = cursor_position - player_position;
let cursor_distance_to_player = cursor_position.distance(player_position);
let velocity_rate = cursor_distance_to_player.min(300.) / 300.;

if cursor_to_player_vector != Vec2::ZERO {
let direction = cursor_to_player_vector.normalize();

player_transform.translation.x += direction.x * 15. * velocity_rate;
player_transform.translation.y += direction.y * 15. * velocity_rate;

if direction != Vec2::ZERO {
let angle = (direction).angle_between(Vec2::X);

if angle.is_finite() {
// FIXME: Rotate the image sprite to always face right?
// FRAC_PI_2 is subtracted to offset the 90 degree rotation from the X axis the sprite has.
player_transform.rotation = Quat::from_rotation_z(-angle - FRAC_PI_2);
}
}
}
player_movement_event_writer.send(PlayerMovementEvent::accelerate(cursor_position));
}
} else if mouse_input.just_released(MouseButton::Right) {
player_movement_event_writer.send(PlayerMovementEvent::brake());
}
}

0 comments on commit 505e150

Please sign in to comment.