Skip to content

Commit

Permalink
Properly control object altitude
Browse files Browse the repository at this point in the history
  • Loading branch information
Indy2222 committed Nov 26, 2022
1 parent 9b5530f commit 612dff1
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 34 deletions.
3 changes: 3 additions & 0 deletions assets/objects/attacker.obj.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@
"range": 50.0,
"damage": 3.0,
"recharge_interval": 2.5
},
"movement": {
"max_heigth": 5.0
}
}
86 changes: 86 additions & 0 deletions crates/movement/src/altitude.rs
Original file line number Diff line number Diff line change
@@ -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<Entity, (With<MovableSolid>, Without<DesiredClimbing>)>,
) {
for entity in objects.iter() {
commands.entity(entity).insert(DesiredClimbing::default());
}
}

fn update(
mut objects: Query<(
&DesiredVelocity<RepulsionVelocity>,
&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);
}
});
}
59 changes: 41 additions & 18 deletions crates/movement/src/kinematics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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),
),
);
}
Expand All @@ -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) {
Expand All @@ -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),
}
}
Expand All @@ -96,18 +111,21 @@ fn kinematics(
time: Res<Time>,
mut objects: Query<(
&DesiredVelocity<RepulsionVelocity>,
&DesiredClimbing,
&mut Kinematics,
&mut ObjectVelocity,
)>,
) {
let time_delta = time.delta_seconds();

objects.par_for_each_mut(512, |(movement, mut kinematics, mut velocity)| {
let desired_velocity = movement.velocity();
let desired_heading = if desired_velocity == Vec2::ZERO {
objects.par_for_each_mut(512, |(movement, climbing, mut kinematics, mut velocity)| {
let desired_horizontal_velocity = movement.velocity();
let desired_heading = if desired_horizontal_velocity == Vec2::ZERO {
kinematics.heading()
} else {
desired_velocity.y.atan2(desired_velocity.x)
desired_horizontal_velocity
.y
.atan2(desired_horizontal_velocity.x)
};

let heading_diff = normalize_angle(desired_heading - kinematics.heading());
Expand All @@ -116,15 +134,20 @@ fn kinematics(
kinematics.update_heading(heading_delta);

let max_speed_delta = MAX_ACCELERATION * time_delta;
let speed_delta = if (heading_diff - heading_delta).abs() > FRAC_PI_4 {
let horizontal_speed_delta = if (heading_diff - heading_delta).abs() > FRAC_PI_4 {
// Slow down if not going in roughly good direction.
-kinematics.speed()
-kinematics.horizontal_speed()
} else {
desired_velocity.length() - kinematics.speed()
desired_horizontal_velocity.length() - kinematics.horizontal_speed()
}
.clamp(-max_speed_delta, max_speed_delta);

kinematics.update_speed(speed_delta);
kinematics.update_horizontal_speed(horizontal_speed_delta);

let vertical_speed_delta = (climbing.speed() - kinematics.vertical_speed())
.clamp(-max_speed_delta, max_speed_delta);
kinematics.update_vertical_speed(vertical_speed_delta);

velocity.update(kinematics.compute_velocity(), kinematics.heading());
});
}
Expand Down
13 changes: 10 additions & 3 deletions crates/movement/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod altitude;
mod cache;
mod disc;
mod kinematics;
Expand All @@ -8,19 +9,24 @@ mod repulsion;

use std::f32::consts::PI;

use altitude::AltitudePlugin;
use bevy::{app::PluginGroupBuilder, prelude::PluginGroup};
use kinematics::KinematicsPlugin;
use movement::MovementPlugin;
use obstacles::ObstaclesPlugin;
use pathing::PathingPlugin;
use repulsion::RepulsionPlugin;

/// Maximum object speed in meters per second.
const MAX_SPEED: f32 = 10.;
/// Maximum object horizontal speed in meters per second.
const MAX_HORIZONTAL_SPEED: f32 = 10.;
/// Maximum object vertical ascending / descending rate in meters per second.
const MAX_VERTICAL_SPEED: f32 = 1.;
/// Maximum object acceleration in meters per second squared.
const MAX_ACCELERATION: f32 = 2. * MAX_SPEED;
const MAX_ACCELERATION: f32 = 2. * MAX_HORIZONTAL_SPEED;
/// Maximum object angular velocity in radians per second.
const MAX_ANGULAR_SPEED: f32 = PI;
/// Maximum altitude in meters (note that this is not height).
const MAX_ALTITUDE: f32 = 100.;

pub struct MovementPluginGroup;

Expand All @@ -32,5 +38,6 @@ impl PluginGroup for MovementPluginGroup {
.add(ObstaclesPlugin)
.add(RepulsionPlugin)
.add(KinematicsPlugin)
.add(AltitudePlugin)
}
}
11 changes: 6 additions & 5 deletions crates/movement/src/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ use std::marker::PhantomData;
use bevy::prelude::*;
use de_core::{
objects::MovableSolid,
projection::{ToAltitude, ToFlat},
projection::ToAltitude,
stages::GameStage,
state::{AppState, GameState},
};
use de_map::size::MapBounds;
use de_objects::EXCLUSION_OFFSET;
use iyes_loopless::prelude::*;

use crate::MAX_ALTITUDE;

pub(crate) struct MovementPlugin;

impl Plugin for MovementPlugin {
Expand Down Expand Up @@ -149,8 +151,7 @@ fn update_transform(

fn clamp(bounds: &MapBounds, translation: Vec3) -> Vec3 {
let offset = Vec2::splat(EXCLUSION_OFFSET);
let min = bounds.min() + offset;
let max = bounds.max() - offset;
let clipped = translation.to_flat().clamp(min, max).to_msl();
Vec3::new(clipped.x, translation.y, clipped.z)
let min = (bounds.min() + offset).to_msl();
let max = (bounds.max() - offset).to_altitude(MAX_ALTITUDE);
translation.clamp(min, max)
}
6 changes: 3 additions & 3 deletions crates/movement/src/pathing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use iyes_loopless::prelude::*;

use crate::{
movement::{add_desired_velocity, DesiredVelocity},
MAX_ACCELERATION, MAX_SPEED,
MAX_ACCELERATION, MAX_HORIZONTAL_SPEED,
};

const DESTINATION_ACCURACY: f32 = 0.1;
Expand Down Expand Up @@ -70,9 +70,9 @@ fn follow_path(
objects.par_for_each_mut(512, |(transform, mut path, mut movement)| {
let location = transform.translation.to_flat();
let remaining = path.destination().distance(location);
let advancement = path.advance(location, MAX_SPEED * 0.5);
let advancement = path.advance(location, MAX_HORIZONTAL_SPEED * 0.5);
let direction = (advancement - location).normalize();
let desired_speed = MAX_SPEED.min((2. * remaining * MAX_ACCELERATION).sqrt());
let desired_speed = MAX_HORIZONTAL_SPEED.min((2. * remaining * MAX_ACCELERATION).sqrt());
movement.update(desired_speed * direction);
});
}
4 changes: 2 additions & 2 deletions crates/movement/src/repulsion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
movement::{add_desired_velocity, DesiredVelocity},
obstacles::{MovableObstacles, ObstaclesLables, StaticObstacles},
pathing::{PathVelocity, PathingLabels},
MAX_ACCELERATION, MAX_SPEED,
MAX_ACCELERATION, MAX_HORIZONTAL_SPEED,
};

const MAX_REPULSION_DISTANCE: f32 = 4.0;
Expand Down Expand Up @@ -276,7 +276,7 @@ fn apply(
512,
|(mut repulsion, path_velocity, mut repulsion_velocity)| {
let velocity = repulsion.apply(path_velocity.velocity());
repulsion_velocity.update(velocity.clamp_length_max(MAX_SPEED));
repulsion_velocity.update(velocity.clamp_length_max(MAX_HORIZONTAL_SPEED));
repulsion.clear();
},
);
Expand Down
Loading

0 comments on commit 612dff1

Please sign in to comment.