From 750ac20a53552a5920d59fcad4f6227ac492cf18 Mon Sep 17 00:00:00 2001 From: extrawurst Date: Wed, 9 Apr 2025 19:11:07 +0200 Subject: [PATCH] movement affected by walking in water --- src/control/controller.rs | 14 ++++-- src/main.rs | 2 + src/water.rs | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/water.rs diff --git a/src/control/controller.rs b/src/control/controller.rs index c12f4c5..7705b2d 100644 --- a/src/control/controller.rs +++ b/src/control/controller.rs @@ -50,6 +50,10 @@ pub struct PlayerMovement { #[reflect(Component)] pub struct MovementAcceleration(Scalar); +#[derive(Component, Reflect)] +#[reflect(Component)] +pub struct MovementSpeedFactor(pub f32); + /// The strength of a jump. #[derive(Component, Reflect)] #[reflect(Component)] @@ -85,6 +89,7 @@ pub struct MovementBundle { acceleration: MovementAcceleration, jump_impulse: JumpImpulse, max_slope_angle: MaxSlopeAngle, + factor: MovementSpeedFactor, } impl MovementBundle { @@ -93,6 +98,7 @@ impl MovementBundle { acceleration: MovementAcceleration(acceleration), jump_impulse: JumpImpulse(jump_impulse), max_slope_angle: MaxSlopeAngle(max_slope_angle), + factor: MovementSpeedFactor(1.), } } } @@ -173,6 +179,7 @@ fn movement( controls: Res, mut controllers: Query<( &MovementAcceleration, + &MovementSpeedFactor, &JumpImpulse, &mut LinearVelocity, Has, @@ -194,7 +201,8 @@ fn movement( direction = direction.normalize_or_zero(); - for (movement_acceleration, jump_impulse, mut linear_velocity, is_grounded) in &mut controllers + for (movement_acceleration, factor, jump_impulse, mut linear_velocity, is_grounded) in + &mut controllers { let mut direction = direction.extend(0.0).xzy(); @@ -203,8 +211,8 @@ fn movement( (rig_transform.forward() * direction.z) + (rig_transform.right() * direction.x); } - linear_velocity.x = -direction.x * movement_acceleration.0 * delta_time; - linear_velocity.z = -direction.z * movement_acceleration.0 * delta_time; + linear_velocity.x = -direction.x * movement_acceleration.0 * delta_time * factor.0; + linear_velocity.z = -direction.z * movement_acceleration.0 * delta_time * factor.0; if is_grounded && jump_requested && !*jump_used { linear_velocity.y = jump_impulse.0; diff --git a/src/main.rs b/src/main.rs index 5baafe1..829cf2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod player; mod sounds; mod tb_entities; mod utils; +mod water; use avian3d::prelude::*; use bevy::{ @@ -146,6 +147,7 @@ fn main() { app.add_plugins(cash_heal::plugin); app.add_plugins(debug::plugin); app.add_plugins(utils::observers::plugin); + app.add_plugins(water::plugin); app.init_state::(); diff --git a/src/water.rs b/src/water.rs new file mode 100644 index 0000000..0cbb810 --- /dev/null +++ b/src/water.rs @@ -0,0 +1,93 @@ +use crate::{ + GameState, control::controller::MovementSpeedFactor, global_observer, player::Player, + tb_entities::Water, +}; +use avian3d::prelude::*; +use bevy::prelude::*; + +#[derive(Component, Reflect)] +#[reflect(Component)] +struct WaterSensor; + +#[derive(Event)] +struct PlayerInWater { + player: Entity, + entered: bool, +} + +pub fn plugin(app: &mut App) { + app.add_systems(OnEnter(GameState::Playing), setup); + app.add_systems( + Update, + check_water_collision.run_if(in_state(GameState::Playing)), + ); + global_observer!(app, on_player_water); +} + +fn setup(mut commands: Commands, query: Query<(Entity, &Children), With>) { + for (e, c) in query.iter() { + assert!(c.len() == 1); + let child = c.iter().next().unwrap(); + + commands.entity(e).insert(( + Sensor, + ColliderConstructorHierarchy::new(ColliderConstructor::TrimeshFromMesh), + )); + + commands.entity(*child).insert(WaterSensor); + } +} + +fn check_water_collision( + mut cmds: Commands, + mut collisionstart_events: EventReader, + mut collisionend_events: EventReader, + query_player: Query<&Player>, + query_water: Query<(Entity, &WaterSensor)>, +) { + let start_events = collisionstart_events + .read() + .map(|CollisionStarted(e1, e2)| (true, *e1, *e2)); + let end_events = collisionend_events + .read() + .map(|CollisionEnded(e1, e2)| (false, *e1, *e2)); + + for (started, e1, e2) in start_events.chain(end_events) { + let entities = [e1, e2]; + + let player = entities + .iter() + .find(|e| query_player.contains(**e)) + .copied(); + let water = entities.iter().find(|e| query_water.contains(**e)).copied(); + + if !(player.is_some() && water.is_some()) { + continue; + } + + let Some(player) = player else { + continue; + }; + + cmds.trigger(PlayerInWater { + player, + entered: started, + }); + } +} + +fn on_player_water( + trigger: Trigger, + //TODO: use a sparse set component `InWater` that we attach to the player + // then we can have a movement factor system that reacts on these components to update the factor + // PLUS we can then always adhoc check if a player is `InWater` to play an according sound and such + mut query: Query<&mut MovementSpeedFactor, With>, +) { + let player = trigger.player; + + let Ok(mut factor) = query.get_mut(player) else { + return; + }; + + factor.0 = if trigger.entered { 0.5 } else { 1. }; +}