From 49e94fbb253d8db50d923ee18ccb5717816f204c Mon Sep 17 00:00:00 2001 From: mnmaita <47983254+mnmaita@users.noreply.github.com> Date: Thu, 16 Nov 2023 00:04:10 +0100 Subject: [PATCH 1/3] Implements physics based player movement --- src/game/mod.rs | 2 +- src/game/player.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++-- src/input.rs | 53 +++++---------------------- 3 files changed, 98 insertions(+), 47 deletions(-) diff --git a/src/game/mod.rs b/src/game/mod.rs index ccc85a0..f481d4d 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -17,5 +17,5 @@ pub use constants::*; pub use enemy::Enemy; pub use fire_breath::SpawnFireBreathEvent; pub use level::{BorderTile, Tile}; -pub use player::Player; +pub use player::{Player, PlayerMovementEvent}; pub use plugin::GamePlugin; diff --git a/src/game/player.rs b/src/game/player.rs index 6e5ff2c..761a177 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -5,7 +5,7 @@ use crate::{ animation::{AnimationIndices, AnimationTimer}, camera::{RenderLayer, YSorted}, physics::Speed, - AppState, + playing, AppState, }; use super::{ @@ -18,7 +18,11 @@ pub(super) struct PlayerPlugin; impl Plugin for PlayerPlugin { fn build(&self, app: &mut App) { + app.add_event::(); + app.add_systems(OnEnter(AppState::InGame), spawn_player); + + app.add_systems(FixedUpdate, handle_player_movement_events.run_if(playing())); } } @@ -31,8 +35,12 @@ pub struct PlayerBundle { pub fire_breath_resource: ResourcePool, pub hitpoints: ResourcePool, pub score: Score, - pub speed: Speed, + pub damping: Damping, + pub external_force: ExternalForce, + pub external_impulse: ExternalImpulse, pub marker: Player, + pub rigid_body: RigidBody, + pub speed: Speed, pub render_layers: RenderLayers, pub spritesheet: SpriteSheetBundle, } @@ -59,6 +67,10 @@ fn spawn_player(mut commands: Commands, asset_server: Res) { marker: Player, render_layers: RenderLayers::layer(RenderLayer::Sky.into()), speed: Speed(10.), + damping: Damping::default(), + external_force: ExternalForce::default(), + external_impulse: ExternalImpulse::default(), + rigid_body: RigidBody::Dynamic, spritesheet: SpriteSheetBundle { atlas: TextureAtlas { layout: texture_atlas_layout_handle, @@ -72,3 +84,77 @@ fn spawn_player(mut commands: Commands, asset_server: Res) { player_entity_commands.insert((InGameEntity, YSorted)); } + +#[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, + mut query: Query< + ( + &Transform, + &mut ExternalForce, + &mut ExternalImpulse, + &mut Damping, + ), + With, + >, +) { + 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., + }; + } + } + } +} diff --git a/src/input.rs b/src/input.rs index d7df06d..960ccd3 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,11 +1,8 @@ -use std::{f32::consts::FRAC_PI_2, time::Duration}; - use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow}; -use bevy_rapier2d::prelude::Collider; use crate::{ - animation::AnimationTimer, camera::MainCamera, game::Player, game::SpawnFireBreathEvent, - physics::Speed, playing, AppState, + camera::MainCamera, game::Player, game::PlayerMovementEvent, game::SpawnFireBreathEvent, + playing, AppState, }; pub struct InputPlugin; @@ -16,7 +13,7 @@ impl Plugin for InputPlugin { PreUpdate, ( clear_input.run_if(state_changed::), - (mouse_input, player_movement).run_if(playing()), + mouse_input.run_if(playing()), ) .chain(), ); @@ -54,11 +51,17 @@ struct FireBreathSfx; fn mouse_input( mut commands: Commands, mut spawn_fire_breath_event_writer: EventWriter, + mut player_movement_event_writer: EventWriter, + cursor_world_position_checker: CursorWorldPositionChecker, query: Query<&Transform, With>, sfx_query: Query>, asset_server: Res, mouse_input: ResMut>, ) { + if let Some(cursor_position) = cursor_world_position_checker.cursor_world_position() { + player_movement_event_writer.send(PlayerMovementEvent::accelerate(cursor_position)); + } + if mouse_input.just_pressed(MouseButton::Left) { commands.spawn(( AudioBundle { @@ -103,44 +106,6 @@ fn mouse_input( } } -fn player_movement( - mut query: Query<(&mut Transform, &Speed, &mut AnimationTimer, &Collider), With>, - cursor_world_position_checker: CursorWorldPositionChecker, -) { - if let Some(cursor_position) = cursor_world_position_checker.cursor_world_position() { - let (mut player_transform, player_speed, mut player_animation_timer, player_collider) = - query.single_mut(); - let player_position = player_transform.translation.truncate(); - let cursor_to_player_vector = cursor_position - player_position; - - if cursor_to_player_vector != Vec2::ZERO { - let cursor_distance_to_player = cursor_position.distance(player_position); - let velocity_rate = cursor_distance_to_player.min(300.) / 300.; - let direction = cursor_to_player_vector.normalize(); - - if cursor_distance_to_player > player_collider.as_cuboid().unwrap().half_extents().y { - player_transform.translation.x += direction.x * player_speed.0 * velocity_rate; - player_transform.translation.y += direction.y * player_speed.0 * velocity_rate; - player_animation_timer.set_duration(Duration::from_secs_f32( - 0.2 * player_speed.0 * 0.25 * velocity_rate, - )); - } else { - player_animation_timer.set_duration(Duration::from_secs_f32(0.2)); - } - - 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); - } - } - } - } -} - fn clear_input( mut keyboard_input: ResMut>, mut mouse_input: ResMut>, From 6926d429e9aeefe855a28fe7130870e094ce0eac Mon Sep 17 00:00:00 2001 From: mnmaita <47983254+mnmaita@users.noreply.github.com> Date: Fri, 17 Nov 2023 03:40:03 +0100 Subject: [PATCH 2/3] Tweaks player movement physics values to account for smaller Collider --- src/game/player.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/player.rs b/src/game/player.rs index 761a177..8978e07 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -134,7 +134,7 @@ fn handle_player_movement_events( let angle = target_to_player_vector.angle_between(direction); *damping = Damping::default(); - external_impulse.impulse = direction * velocity_scalar * 1000.; + external_impulse.impulse = direction * velocity_scalar * 125.; let (torque, strafe) = { if is_in_cruise_mode { @@ -142,7 +142,7 @@ fn handle_player_movement_events( damping.angular_damping = 10.; (0., Vec2::ZERO) } else { - (-angle * 250., direction.perp() * -angle * 125_000.) + (-angle * 75., direction.perp() * -angle * 31250.) } }; From fbdc836d2c546588b6340b22fd8a1acc75c9ef03 Mon Sep 17 00:00:00 2001 From: mnmaita <47983254+mnmaita@users.noreply.github.com> Date: Wed, 1 May 2024 18:41:00 +0200 Subject: [PATCH 3/3] Removes strafing and tweaks dragon movement physics --- src/game/player.rs | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/game/player.rs b/src/game/player.rs index 8978e07..aa1c88b 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -36,7 +36,6 @@ pub struct PlayerBundle { pub hitpoints: ResourcePool, pub score: Score, pub damping: Damping, - pub external_force: ExternalForce, pub external_impulse: ExternalImpulse, pub marker: Player, pub rigid_body: RigidBody, @@ -68,7 +67,6 @@ fn spawn_player(mut commands: Commands, asset_server: Res) { render_layers: RenderLayers::layer(RenderLayer::Sky.into()), speed: Speed(10.), damping: Damping::default(), - external_force: ExternalForce::default(), external_impulse: ExternalImpulse::default(), rigid_body: RigidBody::Dynamic, spritesheet: SpriteSheetBundle { @@ -103,17 +101,9 @@ impl PlayerMovementEvent { fn handle_player_movement_events( mut player_movement_event_reader: EventReader, - mut query: Query< - ( - &Transform, - &mut ExternalForce, - &mut ExternalImpulse, - &mut Damping, - ), - With, - >, + mut query: Query<(&Transform, &mut ExternalImpulse, &mut Damping), With>, ) { - let (transform, mut external_force, mut external_impulse, mut damping) = query.single_mut(); + let (transform, mut external_impulse, mut damping) = query.single_mut(); for event in player_movement_event_reader.read() { match event { @@ -130,24 +120,24 @@ fn handle_player_movement_events( 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 is_facing_forward = (-0.4..0.4).contains(&angle_with_cursor); + let is_in_cruise_mode = (-0.2..0.2).contains(&angle_with_cursor); let angle = target_to_player_vector.angle_between(direction); - *damping = Damping::default(); external_impulse.impulse = direction * velocity_scalar * 125.; - let (torque, strafe) = { + if is_facing_forward { + damping.angular_damping = 3.; + external_impulse.torque_impulse = 0.; + external_impulse.impulse *= 2.; if is_in_cruise_mode { - external_impulse.impulse *= 2.; - damping.angular_damping = 10.; - (0., Vec2::ZERO) - } else { - (-angle * 75., direction.perp() * -angle * 31250.) + damping.angular_damping = 5.; } - }; - - external_impulse.torque_impulse = torque; - external_force.force = strafe; + } else { + damping.angular_damping = 0.5; + external_impulse.torque_impulse = -angle * 2.; + damping.linear_damping = 1.5; + } } PlayerMovementEvent::Brake => { *damping = Damping {