From 612dff13ee55547e51623990c12cec60fca81824 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Sat, 26 Nov 2022 15:12:24 +0100 Subject: [PATCH] Properly control object altitude Fixes #262. --- assets/objects/attacker.obj.json | 3 ++ crates/movement/src/altitude.rs | 86 +++++++++++++++++++++++++++++++ crates/movement/src/kinematics.rs | 59 ++++++++++++++------- crates/movement/src/lib.rs | 13 +++-- crates/movement/src/movement.rs | 11 ++-- crates/movement/src/pathing.rs | 6 +-- crates/movement/src/repulsion.rs | 4 +- crates/objects/src/cache.rs | 25 +++++++-- crates/objects/src/lib.rs | 2 + crates/objects/src/loader.rs | 16 ++++++ crates/objects/src/movement.rs | 21 ++++++++ docs/content/objects/schema.json | 11 ++++ 12 files changed, 223 insertions(+), 34 deletions(-) create mode 100644 crates/movement/src/altitude.rs create mode 100644 crates/objects/src/movement.rs diff --git a/assets/objects/attacker.obj.json b/assets/objects/attacker.obj.json index c5a81e704..89e4ba7bc 100644 --- a/assets/objects/attacker.obj.json +++ b/assets/objects/attacker.obj.json @@ -42,5 +42,8 @@ "range": 50.0, "damage": 3.0, "recharge_interval": 2.5 + }, + "movement": { + "max_heigth": 5.0 } } diff --git a/crates/movement/src/altitude.rs b/crates/movement/src/altitude.rs new file mode 100644 index 000000000..d857b802e --- /dev/null +++ b/crates/movement/src/altitude.rs @@ -0,0 +1,86 @@ +use bevy::prelude::*; +use de_core::{ + objects::MovableSolid, + stages::GameStage, + state::{AppState, GameState}, +}; +use iyes_loopless::prelude::*; + +use crate::{ + movement::DesiredVelocity, + repulsion::{RepulsionLables, RepulsionVelocity}, + MAX_ACCELERATION, MAX_VERTICAL_SPEED, +}; + +pub(crate) struct AltitudePlugin; + +impl Plugin for AltitudePlugin { + fn build(&self, app: &mut App) { + app.add_system_to_stage( + GameStage::PreMovement, + setup_entities.run_in_state(AppState::InGame), + ) + .add_system_set_to_stage( + GameStage::Movement, + SystemSet::new().with_system( + update + .run_in_state(GameState::Playing) + .label(AltitudeLabels::Update) + .after(RepulsionLables::Apply), + ), + ); + } +} + +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] +pub(crate) enum AltitudeLabels { + Update, +} + +#[derive(Component, Default)] +pub(crate) struct DesiredClimbing(f32); + +impl DesiredClimbing { + pub(crate) fn speed(&self) -> f32 { + self.0 + } + + pub(crate) fn set_speed(&mut self, speed: f32) { + self.0 = speed; + } +} + +fn setup_entities( + mut commands: Commands, + objects: Query, Without)>, +) { + for entity in objects.iter() { + commands.entity(entity).insert(DesiredClimbing::default()); + } +} + +fn update( + mut objects: Query<( + &DesiredVelocity, + &mut DesiredClimbing, + &Transform, + )>, +) { + objects.par_for_each_mut(512, |(horizontal, mut climbing, transform)| { + let desired_height = if horizontal.stationary() { + 0. + } else { + // TODO use max_height + 10. + }; + + let remaining = desired_height - transform.translation.y; + let desired = remaining.signum() + * MAX_VERTICAL_SPEED.min((2. * remaining.abs() * MAX_ACCELERATION).sqrt()); + + // Avoid change detection when possible. + if climbing.speed() != desired { + climbing.set_speed(desired); + } + }); +} diff --git a/crates/movement/src/kinematics.rs b/crates/movement/src/kinematics.rs index 3d9a18223..0adc69450 100644 --- a/crates/movement/src/kinematics.rs +++ b/crates/movement/src/kinematics.rs @@ -10,9 +10,10 @@ use de_core::{ use iyes_loopless::prelude::*; use crate::{ + altitude::{AltitudeLabels, DesiredClimbing}, movement::{DesiredVelocity, MovementLabels, ObjectVelocity}, repulsion::{RepulsionLables, RepulsionVelocity}, - MAX_ACCELERATION, MAX_ANGULAR_SPEED, MAX_SPEED, + MAX_ACCELERATION, MAX_ANGULAR_SPEED, MAX_HORIZONTAL_SPEED, MAX_VERTICAL_SPEED, }; pub(crate) struct KinematicsPlugin; @@ -30,7 +31,8 @@ impl Plugin for KinematicsPlugin { .run_in_state(GameState::Playing) .label(KinematicsLabels::Kinematics) .before(MovementLabels::UpdateTransform) - .after(RepulsionLables::Apply), + .after(RepulsionLables::Apply) + .after(AltitudeLabels::Update), ), ); } @@ -46,24 +48,36 @@ type Uninitialized<'w, 's> = #[derive(Component)] struct Kinematics { - /// Current speed in meters per second. - speed: f32, + /// Current horizontal speed in meters per second. + horizontal_speed: f32, + /// Current vertical speed in meters per second. + vertical_speed: f32, /// Current object heading in radians. heading: f32, } impl Kinematics { - fn speed(&self) -> f32 { - self.speed + fn horizontal_speed(&self) -> f32 { + self.horizontal_speed + } + + fn vertical_speed(&self) -> f32 { + self.vertical_speed } fn heading(&self) -> f32 { self.heading } - fn update_speed(&mut self, delta: f32) { + fn update_horizontal_speed(&mut self, delta: f32) { debug_assert!(delta.is_finite()); - self.speed = (self.speed + delta).clamp(0., MAX_SPEED); + self.horizontal_speed = (self.horizontal_speed + delta).clamp(0., MAX_HORIZONTAL_SPEED); + } + + fn update_vertical_speed(&mut self, delta: f32) { + debug_assert!(delta.is_finite()); + self.vertical_speed = + (self.vertical_speed + delta).clamp(-MAX_VERTICAL_SPEED, MAX_VERTICAL_SPEED); } fn update_heading(&mut self, delta: f32) { @@ -73,14 +87,15 @@ impl Kinematics { fn compute_velocity(&self) -> Vec3 { let (sin, cos) = self.heading.sin_cos(); - Vec2::new(self.speed * cos, self.speed * sin).to_msl() + (self.horizontal_speed * Vec2::new(cos, sin)).to_altitude(self.vertical_speed) } } impl From<&Transform> for Kinematics { fn from(transform: &Transform) -> Self { Self { - speed: 0., + horizontal_speed: 0., + vertical_speed: 0., heading: normalize_angle(transform.rotation.to_euler(EulerRot::YXZ).0), } } @@ -96,18 +111,21 @@ fn kinematics( time: Res