Add system to de-intersect wheels and planet surface.

This commit is contained in:
Joe Ardent 2023-01-30 16:05:42 -08:00
parent 79b2d4b175
commit 7efa0a8ab2
5 changed files with 112 additions and 31 deletions

View file

@ -1,7 +1,7 @@
use std::time::Instant; use std::time::Instant;
use bevy::{ use bevy::{
prelude::{Component, ReflectResource, Resource}, prelude::{Component, ReflectResource, Resource, Vec3},
reflect::Reflect, reflect::Reflect,
}; };
@ -14,6 +14,21 @@ impl Default for ActionDebugInstant {
} }
} }
#[derive(Debug, Component)]
pub(super) struct Tunneling {
pub frames: usize,
pub dir: Vec3,
}
impl Default for Tunneling {
fn default() -> Self {
Tunneling {
frames: 15,
dir: Vec3::ZERO,
}
}
}
#[derive(Debug, Resource, Reflect)] #[derive(Debug, Resource, Reflect)]
#[reflect(Resource)] #[reflect(Resource)]
pub struct MovementSettings { pub struct MovementSettings {
@ -27,7 +42,7 @@ impl Default for MovementSettings {
Self { Self {
sensitivity: 6.0, sensitivity: 6.0,
accel: 40.0, accel: 40.0,
gravity: 7.0, gravity: 9.8,
} }
} }
} }
@ -68,7 +83,7 @@ impl Default for CatControllerState {
roll_prev: Default::default(), roll_prev: Default::default(),
pitch_integral: Default::default(), pitch_integral: Default::default(),
pitch_prev: Default::default(), pitch_prev: Default::default(),
decay_factor: 0.9, decay_factor: 0.99,
roll_limit: 1.5, roll_limit: 1.5,
pitch_limit: 1.2, pitch_limit: 1.2,
} }
@ -77,10 +92,10 @@ impl Default for CatControllerState {
impl CatControllerState { impl CatControllerState {
pub fn decay(&mut self) { pub fn decay(&mut self) {
if self.roll_integral.abs() > 0.1 { if self.roll_integral.abs() > 0.001 {
self.roll_integral *= self.decay_factor; self.roll_integral *= self.decay_factor;
} }
if self.pitch_integral.abs() > 0.1 { if self.pitch_integral.abs() > 0.001 {
self.pitch_integral *= self.decay_factor; self.pitch_integral *= self.decay_factor;
} }
} }
@ -101,7 +116,7 @@ impl CatControllerState {
(derivative, self.pitch_integral) (derivative, self.pitch_integral)
} }
pub fn set_limits(&mut self, roll: f32, pitch: f32) { pub fn set_integral_limits(&mut self, roll: f32, pitch: f32) {
self.roll_limit = roll; self.roll_limit = roll;
self.pitch_limit = pitch; self.pitch_limit = pitch;
} }

View file

@ -21,9 +21,17 @@ impl Plugin for CyberActionPlugin {
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default()) .add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_startup_system(zero_gravity) .add_startup_system(zero_gravity)
.add_system(surface_fix)
.add_system(gravity.before("cat")) .add_system(gravity.before("cat"))
.add_system(falling_cat.label("cat")) .add_system(falling_cat.label("cat"))
.add_system(input_forces.label("iforces").after("cat")) .add_system(input_forces.label("iforces").after("cat"))
.add_system(
tunnel_out
.label("tunnel")
.before("surface_fix")
.after("drag"),
)
.add_system(surface_fix.label("surface_fix").after("cat"))
.add_system(drag.label("drag").after("iforces")); .add_system(drag.label("drag").after("iforces"));
} }
} }

View file

@ -1,35 +1,38 @@
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
use std::time::Instant; use std::time::Instant;
use bevy::prelude::{Query, Res, ResMut, Time, Transform, Vec3, With}; use bevy::prelude::{Commands, Entity, Query, Res, ResMut, Time, Transform, Vec3, With, Without};
use bevy_rapier3d::prelude::{ExternalForce, Friction, RapierConfiguration, Velocity}; use bevy_rapier3d::prelude::{
CollisionGroups, ExternalForce, Friction, Group, QueryFilter, RapierConfiguration,
RapierContext, ReadMassProperties, Velocity,
};
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
use super::ActionDebugInstant; use super::ActionDebugInstant;
use super::{CatControllerSettings, CatControllerState, MovementSettings}; use super::{CatControllerSettings, CatControllerState, MovementSettings, Tunneling};
use crate::{ use crate::{
bike::{CyberBikeBody, CyberWheel}, bike::{CyberBikeBody, CyberWheel, BIKE_WHEEL_COLLISION_GROUP},
input::InputState, input::InputState,
}; };
/// Disable gravity in Rapier. /// Disable gravity in Rapier.
pub(crate) fn zero_gravity(mut config: ResMut<RapierConfiguration>) { pub(super) fn zero_gravity(mut config: ResMut<RapierConfiguration>) {
config.gravity = Vec3::ZERO; config.gravity = Vec3::ZERO;
} }
/// The gravity vector points from the cyberbike to the center of the planet. /// The gravity vector points from the cyberbike to the center of the planet.
pub(crate) fn gravity( pub(super) fn gravity(
mut query: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>, mut query: Query<(&Transform, &mut ExternalForce, &ReadMassProperties), With<CyberBikeBody>>,
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
) { ) {
let (xform, mut forces) = query.single_mut(); let (xform, mut forces, mprops) = query.single_mut();
let grav = xform.translation.normalize() * -settings.gravity; let grav = xform.translation.normalize() * -settings.gravity * mprops.0.mass;
forces.force = grav; forces.force = grav;
forces.torque = Vec3::ZERO; forces.torque = Vec3::ZERO;
} }
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright. /// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(crate) fn falling_cat( pub(super) fn falling_cat(
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>, mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>,
time: Res<Time>, time: Res<Time>,
settings: Res<CatControllerSettings>, settings: Res<CatControllerSettings>,
@ -58,14 +61,14 @@ pub(crate) fn falling_cat(
let (derivative, integral) = control_vars.update_pitch(pitch_error, time.delta_seconds()); let (derivative, integral) = control_vars.update_pitch(pitch_error, time.delta_seconds());
let mag = (settings.kp * pitch_error) + (settings.ki * integral) + (settings.kd * derivative); let mag = (settings.kp * pitch_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() { if mag.is_finite() {
forces.torque += wright * mag * 0.2; forces.torque += wright * mag;
} }
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
{ {
let now = Instant::now(); let now = Instant::now();
if (now - debug_instant.0).as_millis() > 500 { if (now - debug_instant.0).as_millis() > 500 {
dbg!(roll_error, pitch_error, &control_vars); dbg!(roll_error, pitch_error, &control_vars, mag);
debug_instant.0 = now; debug_instant.0 = now;
} }
} }
@ -73,7 +76,7 @@ pub(crate) fn falling_cat(
} }
/// Apply forces to the cyberbike as a result of input. /// Apply forces to the cyberbike as a result of input.
pub(crate) fn input_forces( pub(super) fn input_forces(
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
input: Res<InputState>, input: Res<InputState>,
mut cquery: Query<&mut Friction, With<CyberWheel>>, mut cquery: Query<&mut Friction, With<CyberWheel>>,
@ -97,9 +100,63 @@ pub(crate) fn input_forces(
*forces += force; *forces += force;
} }
/// Don't let the wheels get stuck underneat the planet
pub(super) fn surface_fix(
mut commands: Commands,
mut wheel_query: Query<
(Entity, &Transform, &mut CollisionGroups),
(With<CyberWheel>, Without<Tunneling>),
>,
context: Res<RapierContext>,
) {
// assume the body is not below the planet surface
for (entity, xform, mut cgroups) in wheel_query.iter_mut() {
let ray_dir = xform.translation.normalize();
if let Some(hit) = context.cast_ray_and_get_normal(
xform.translation,
ray_dir,
10.0,
false,
QueryFilter::only_fixed(),
) {
cgroups.memberships = Group::NONE;
cgroups.filters = Group::NONE;
commands.entity(entity).insert(Tunneling {
frames: 15,
dir: -hit.1.normal,
});
}
}
}
pub(super) fn tunnel_out(
mut commands: Commands,
mut wheel_query: Query<
(
Entity,
&mut Tunneling,
&mut CollisionGroups,
&mut ExternalForce,
),
With<CyberWheel>,
>,
) {
for (entity, mut tunneling, mut cgroups, mut force) in wheel_query.iter_mut() {
if tunneling.frames == 0 {
commands.entity(entity).remove::<Tunneling>();
cgroups.memberships = BIKE_WHEEL_COLLISION_GROUP.0;
cgroups.filters = BIKE_WHEEL_COLLISION_GROUP.1;
continue;
}
tunneling.frames -= 1;
force.force += tunneling.dir * tunneling.frames as f32;
dbg!(&tunneling);
}
}
/// General velocity-based drag-force calculation; does not take orientation /// General velocity-based drag-force calculation; does not take orientation
/// into account. /// into account.
pub(crate) fn drag(mut query: Query<(&Velocity, &mut ExternalForce), With<CyberBikeBody>>) { pub(super) fn drag(mut query: Query<(&Velocity, &mut ExternalForce), With<CyberBikeBody>>) {
let (vels, mut forces) = query.single_mut(); let (vels, mut forces) = query.single_mut();
if let Some(vel) = vels.linvel.try_normalize() { if let Some(vel) = vels.linvel.try_normalize() {

View file

@ -5,8 +5,8 @@ use bevy_rapier3d::{
geometry::Group, geometry::Group,
prelude::{ prelude::{
Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction, Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction,
MultibodyJoint, PrismaticJointBuilder, Restitution, RigidBody, Sleeping, MultibodyJoint, PrismaticJointBuilder, ReadMassProperties, Restitution, RigidBody,
TransformInterpolation, Velocity, Sleeping, TransformInterpolation, Velocity,
}, },
}; };
@ -42,17 +42,17 @@ impl Default for WheelConfig {
front_forward: 0.9, front_forward: 0.9,
front_stance: 0.65, front_stance: 0.65,
rear_back: 1.1, rear_back: 1.1,
y: -0.4, y: -0.45,
limits: [-0.5, 0.1], limits: [-0.7, 0.1],
stiffness: 75.0, stiffness: 90.0,
damping: 2.0, damping: 8.0,
radius: 0.3, radius: 0.3,
} }
} }
} }
const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1); pub const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1);
const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10); pub const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10);
fn spawn_cyberbike( fn spawn_cyberbike(
mut commands: Commands, mut commands: Commands,
@ -89,7 +89,7 @@ fn spawn_cyberbike(
..Default::default() ..Default::default()
}; };
let mass_properties = ColliderMassProperties::Density(0.2); let mass_properties = ColliderMassProperties::Density(0.9);
let (membership, filter) = BIKE_BODY_COLLISION_GROUP; let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
let bike_collision_group = CollisionGroups::new(membership, filter); let bike_collision_group = CollisionGroups::new(membership, filter);
@ -113,6 +113,7 @@ fn spawn_cyberbike(
friction, friction,
not_sleeping, not_sleeping,
ccd, ccd,
ReadMassProperties::default(),
)) ))
.insert(TransformInterpolation { .insert(TransformInterpolation {
start: None, start: None,

View file

@ -70,8 +70,8 @@ fn follow_cyberbike(
for (mut cam_xform, cam_type) in query.p1().iter_mut() { for (mut cam_xform, cam_type) in query.p1().iter_mut() {
match *cam_type { match *cam_type {
CyberCameras::Hero => { CyberCameras::Hero => {
let look_at = bike_xform.translation + (bike_xform.forward() * 200.0); let look_at = bike_xform.translation + (bike_xform.forward() * 500.0);
let cam_pos = bike_xform.translation + (bike_xform.back() * 1.05) + (up * 1.08); let cam_pos = bike_xform.translation + (bike_xform.back() * 0.1) + (up * 0.8);
cam_xform.translation = cam_pos; cam_xform.translation = cam_pos;
cam_xform.look_at(look_at, up); cam_xform.look_at(look_at, up);