From c924428a9edfffa8aa81bd6fab16c0e262bdedb9 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Tue, 18 Feb 2025 22:24:44 -0800 Subject: [PATCH] bouncy suspension, no friction --- src/bike.rs | 23 ++++++---- src/physics.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/src/bike.rs b/src/bike.rs index 018791b..6016dd9 100644 --- a/src/bike.rs +++ b/src/bike.rs @@ -5,12 +5,15 @@ use bevy::prelude::*; use crate::physics::CatControllerState; -pub const REST_DISTANCE: f32 = 0.8; +pub const REST_DISTANCE: f32 = 1.0; +pub const WHEEL_RADIUS: f32 = 0.4; +pub const FRONT_ATTACH: Vec3 = Vec3::new(0.0, 0.0, -0.7); +pub const REAR_ATTACH: Vec3 = Vec3::new(0.0, 0.0, 0.7); #[derive(Component)] pub struct CyberBikeBody; -#[derive(Component)] +#[derive(Component, Clone, Copy, Debug)] pub enum CyberWheel { Front, Rear, @@ -25,18 +28,23 @@ pub struct Steering; pub struct Rearing; #[derive(Debug, Resource, Reflect)] +#[reflect(Resource)] pub struct SuspensionConfig { pub rest_dist: f32, pub konstant: f32, pub damping: f32, + pub front_attach: Vec3, + pub rear_attach: Vec3, } impl Default for SuspensionConfig { fn default() -> Self { Self { rest_dist: REST_DISTANCE, - konstant: 150.0, + konstant: 50.0, damping: 50.0, + front_attach: FRONT_ATTACH, + rear_attach: REAR_ATTACH, } } } @@ -46,7 +54,7 @@ fn spawn_bike( mut meshes: ResMut>, mut materials: ResMut>, ) { - let pos = Vec3::new(0.0, 24.0, 0.0); + let pos = Vec3::new(0.0, 4.0, 0.0); let xform = Transform::from_translation(pos); //.with_rotation(Quat::from_rotation_z(0.0)); let body_collider = @@ -86,8 +94,6 @@ fn spawn_bike( builder.parent_entity(), ); }); - - //spawn_wheels(commands, meshes, materials, xform, bike); } fn spawn_caster( @@ -98,10 +104,10 @@ fn spawn_caster( parent: Entity, is_front: bool, ) { - let caster = ShapeCaster::new(collider, xform.translation, Quat::IDENTITY, direction) + let caster = ShapeCaster::new(collider, Vec3::ZERO, Quat::IDENTITY, direction) .with_query_filter(SpatialQueryFilter::from_excluded_entities([parent])); - let mut entity = commands.spawn((caster, Transform::default())); + let mut entity = commands.spawn((caster, xform)); if is_front { entity.insert((Steering, Name::new("front wheel"))); } else { @@ -194,6 +200,7 @@ fn wheel_mesh( Mesh3d(meshes.add(tire_mesh)), MeshMaterial3d(materials.add(wheel_material.clone())), xform, + LinearVelocity::ZERO, wheel, )); } diff --git a/src/physics.rs b/src/physics.rs index 88e2d0b..2f22c7f 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -62,10 +62,13 @@ mod systems { use std::f32::consts::{FRAC_PI_3, FRAC_PI_4}; use avian3d::prelude::*; - use bevy::prelude::*; + use bevy::{math::VectorSpace, prelude::*}; use super::{CatControllerSettings, CatControllerState, CyberLean}; - use crate::{bike::CyberBikeBody, input::InputState}; + use crate::{ + bike::{CyberBikeBody, CyberWheel, Rearing, Steering, SuspensionConfig}, + input::InputState, + }; fn _yaw_to_angle(yaw: f32) -> f32 { let v = yaw.powi(5) * FRAC_PI_3; @@ -166,9 +169,115 @@ mod systems { 0.0 } } + + pub(super) fn suspension( + mut bike_body_query: Query<(&Transform, &mut ExternalForce), With>, + mut wheel_mesh_query: Query< + (&mut Transform, &LinearVelocity, &CyberWheel), + Without, + >, + front_wheel_query: Query<(&ShapeCaster, &ShapeHits), (With, Without)>, + rear_wheel_query: Query<(&ShapeCaster, &ShapeHits), (With, Without)>, + config: Res, + ) { + let mut front_wheel_xform = None; + let mut rear_wheel_xform = None; + let mut front_wheel_vel = Vec3::ZERO; + let mut rear_wheel_vel = Vec3::ZERO; + for (xform, vel, wheel) in wheel_mesh_query.iter_mut() { + match wheel { + CyberWheel::Front => { + front_wheel_xform = Some(xform); + front_wheel_vel = **vel; + } + CyberWheel::Rear => { + rear_wheel_xform = Some(xform); + rear_wheel_vel = **vel; + } + } + } + + let (bike_xform, mut bike_forces) = bike_body_query.single_mut(); + + for (caster, hits) in front_wheel_query.iter() { + for hit in hits.iter() { + if let Some(ref mut xform) = front_wheel_xform { + let force = suspension_force( + caster, + hit, + &config, + front_wheel_vel, + xform, + bike_xform.rotation, + true, + ); + bike_forces.apply_force_at_point( + force, + caster.global_origin(), + bike_xform.translation, + ); + } + } + } + + for (caster, hits) in rear_wheel_query.iter() { + for hit in hits.iter() { + if let Some(ref mut xform) = rear_wheel_xform { + let force = suspension_force( + caster, + hit, + &config, + rear_wheel_vel, + xform, + bike_xform.rotation, + false, + ); + bike_forces.apply_force_at_point( + force, + caster.global_origin(), + bike_xform.translation, + ); + } + } + } + } + + fn suspension_force( + caster: &ShapeCaster, + hit: &ShapeHitData, + config: &SuspensionConfig, + wheel_vel: Vec3, + wheel_xform: &mut Transform, + rotation: Quat, + is_front: bool, + ) -> Vec3 { + let mut loc = Vec3::ZERO; + let mut up_force = Vec3::ZERO; + let attach = if is_front { + config.front_attach + } else { + config.rear_attach + }; + let dist = hit.distance; + let cdir = caster.direction.as_vec3(); + let dir = rotation.mul_vec3(cdir); + if dist < config.rest_dist { + loc = attach + (cdir * dist); + let displacement = config.rest_dist - dist; + let damper_vel = dir.dot(wheel_vel); + let mag = config.konstant * displacement - config.damping * damper_vel; + up_force = -dir * mag; + } else { + loc = attach + (cdir * config.rest_dist); + } + + wheel_xform.translation = loc; + + up_force + } } -use systems::{apply_lean, calculate_lean}; +use systems::{apply_lean, calculate_lean, suspension}; pub struct CyberPhysicsPlugin; @@ -184,6 +293,6 @@ impl Plugin for CyberPhysicsPlugin { .add_systems(Startup, |mut gravity: ResMut| { gravity.0 *= 0.01; }) - .add_systems(Update, (apply_lean, calculate_lean)); + .add_systems(Update, (apply_lean, calculate_lean, suspension)); } }