quasi-merge from rolling_wheels
This commit is contained in:
parent
e79661b7db
commit
e666c972ac
12 changed files with 682 additions and 482 deletions
143
src/action.rs
143
src/action.rs
|
@ -1,143 +0,0 @@
|
||||||
use bevy::{
|
|
||||||
diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use bevy_rapier3d::prelude::{
|
|
||||||
ExternalForce, Friction, NoUserData, RapierConfiguration, RapierPhysicsPlugin, Velocity,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bike::{CyberBikeBody, CyberBikeCollider, CyberBikeControl},
|
|
||||||
input::InputState,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct MovementSettings {
|
|
||||||
pub accel: f32,
|
|
||||||
pub gravity: f32,
|
|
||||||
pub sensitivity: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MovementSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
sensitivity: 10.0,
|
|
||||||
accel: 20.0,
|
|
||||||
gravity: 9.8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Resource, Reflect)]
|
|
||||||
#[reflect(Resource)]
|
|
||||||
struct CatControllerSettings {
|
|
||||||
pub kp: f32,
|
|
||||||
pub kd: f32,
|
|
||||||
pub kws: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CatControllerSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
kp: 10.0,
|
|
||||||
kd: 4.0,
|
|
||||||
kws: 0.85,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zero_gravity(mut config: ResMut<RapierConfiguration>) {
|
|
||||||
config.gravity = Vec3::ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gravity(
|
|
||||||
mut query: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>,
|
|
||||||
settings: Res<MovementSettings>,
|
|
||||||
) {
|
|
||||||
let (_xform, mut forces) = query.single_mut();
|
|
||||||
let grav = Vec3::Y * -settings.gravity;
|
|
||||||
forces.force = grav;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn falling_cat(
|
|
||||||
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CyberBikeControl)>,
|
|
||||||
_diagnostics: Res<Diagnostics>,
|
|
||||||
settings: Res<CatControllerSettings>,
|
|
||||||
) {
|
|
||||||
let (xform, mut forces, mut control_vars) = bike_query.single_mut();
|
|
||||||
let up = Vec3::Y;
|
|
||||||
let bike_up = xform.up();
|
|
||||||
|
|
||||||
let torque = bike_up.cross(up).normalize_or_zero();
|
|
||||||
let cos = up.dot(bike_up);
|
|
||||||
let cos = if cos.is_finite() { cos } else { 1.0 };
|
|
||||||
|
|
||||||
let error = 1.0 - cos;
|
|
||||||
|
|
||||||
let derivative = error - control_vars.prev_error;
|
|
||||||
control_vars.prev_error = error;
|
|
||||||
// this integral term is not an integral, it's more like a weighted moving sum
|
|
||||||
let weighted_sum = control_vars.error_sum + error;
|
|
||||||
control_vars.error_sum = weighted_sum * 0.8;
|
|
||||||
|
|
||||||
let mag = (settings.kp * error) + (settings.kws * weighted_sum) + (settings.kd * derivative);
|
|
||||||
|
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
if let Some(count) = _diagnostics
|
|
||||||
.get(FrameTimeDiagnosticsPlugin::FRAME_COUNT)
|
|
||||||
.and_then(|d| d.smoothed())
|
|
||||||
.map(|x| x as u64)
|
|
||||||
{
|
|
||||||
if count % 30 == 0 {
|
|
||||||
dbg!(&control_vars, mag, cos, derivative);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forces.torque = torque * mag;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input_forces(
|
|
||||||
settings: Res<MovementSettings>,
|
|
||||||
input: Res<InputState>,
|
|
||||||
mut cquery: Query<&mut Friction, With<CyberBikeCollider>>,
|
|
||||||
mut bquery: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>,
|
|
||||||
) {
|
|
||||||
let (xform, mut forces) = bquery.single_mut();
|
|
||||||
let mut friction = cquery.single_mut();
|
|
||||||
|
|
||||||
// thrust
|
|
||||||
let thrust = xform.forward() * input.throttle * settings.accel;
|
|
||||||
forces.force += thrust;
|
|
||||||
|
|
||||||
// brake
|
|
||||||
friction.coefficient = if input.brake { 2.0 } else { 0.0 };
|
|
||||||
|
|
||||||
// steering
|
|
||||||
let torque = xform.down() * input.yaw * settings.sensitivity;
|
|
||||||
forces.torque += torque;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drag(mut query: Query<(&Velocity, &mut ExternalForce), With<CyberBikeBody>>) {
|
|
||||||
let (vels, mut forces) = query.single_mut();
|
|
||||||
|
|
||||||
if let Some(vel) = vels.linvel.try_normalize() {
|
|
||||||
let v2 = vels.linvel.length_squared();
|
|
||||||
forces.force -= vel * (v2 * 0.02);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CyberActionPlugin;
|
|
||||||
impl Plugin for CyberActionPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.init_resource::<MovementSettings>()
|
|
||||||
.init_resource::<CatControllerSettings>()
|
|
||||||
.register_type::<CatControllerSettings>()
|
|
||||||
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
|
|
||||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
|
||||||
.add_startup_system(zero_gravity)
|
|
||||||
.add_system(gravity.before("cat"))
|
|
||||||
.add_system(falling_cat.label("cat"))
|
|
||||||
.add_system(input_forces.label("iforces").after("cat"))
|
|
||||||
.add_system(drag.label("drag").after("iforces"));
|
|
||||||
}
|
|
||||||
}
|
|
123
src/action/components.rs
Normal file
123
src/action/components.rs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
prelude::{Component, ReflectResource, Resource, Vec3},
|
||||||
|
reflect::Reflect,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Resource)]
|
||||||
|
pub(crate) struct ActionDebugInstant(pub Instant);
|
||||||
|
|
||||||
|
impl Default for ActionDebugInstant {
|
||||||
|
fn default() -> Self {
|
||||||
|
ActionDebugInstant(Instant::now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct MovementSettings {
|
||||||
|
pub accel: f32,
|
||||||
|
pub gravity: f32,
|
||||||
|
pub sensitivity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MovementSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
sensitivity: 6.0,
|
||||||
|
accel: 40.0,
|
||||||
|
gravity: 9.8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Resource, Reflect)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct CatControllerSettings {
|
||||||
|
pub kp: f32,
|
||||||
|
pub kd: f32,
|
||||||
|
pub ki: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CatControllerSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
kp: 40.0,
|
||||||
|
kd: 5.0,
|
||||||
|
ki: 0.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Clone, Copy)]
|
||||||
|
pub struct CatControllerState {
|
||||||
|
pub roll_integral: f32,
|
||||||
|
pub roll_prev: f32,
|
||||||
|
pub pitch_integral: f32,
|
||||||
|
pub pitch_prev: f32,
|
||||||
|
decay_factor: f32,
|
||||||
|
roll_limit: f32,
|
||||||
|
pitch_limit: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CatControllerState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
roll_integral: Default::default(),
|
||||||
|
roll_prev: Default::default(),
|
||||||
|
pitch_integral: Default::default(),
|
||||||
|
pitch_prev: Default::default(),
|
||||||
|
decay_factor: 0.99,
|
||||||
|
roll_limit: 1.5,
|
||||||
|
pitch_limit: 0.8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CatControllerState {
|
||||||
|
pub fn decay(&mut self) {
|
||||||
|
if self.roll_integral.abs() > 0.001 {
|
||||||
|
self.roll_integral *= self.decay_factor;
|
||||||
|
}
|
||||||
|
if self.pitch_integral.abs() > 0.001 {
|
||||||
|
self.pitch_integral *= self.decay_factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_roll(&mut self, error: f32, dt: f32) -> (f32, f32) {
|
||||||
|
let lim = self.roll_limit;
|
||||||
|
self.roll_integral = (self.roll_integral + (error * dt)).min(lim).max(-lim);
|
||||||
|
let derivative = (error - self.roll_prev) / dt;
|
||||||
|
self.roll_prev = error;
|
||||||
|
(derivative, self.roll_integral)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_pitch(&mut self, error: f32, dt: f32) -> (f32, f32) {
|
||||||
|
let lim = self.pitch_limit;
|
||||||
|
self.pitch_integral = (self.pitch_integral + (error * dt)).min(lim).max(-lim);
|
||||||
|
let derivative = (error - self.pitch_prev) / dt;
|
||||||
|
self.pitch_prev = error;
|
||||||
|
(derivative, self.pitch_integral)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_integral_limits(&mut self, roll: f32, pitch: f32) {
|
||||||
|
self.roll_limit = roll;
|
||||||
|
self.pitch_limit = pitch;
|
||||||
|
}
|
||||||
|
}
|
37
src/action/mod.rs
Normal file
37
src/action/mod.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use bevy::{
|
||||||
|
diagnostic::FrameTimeDiagnosticsPlugin,
|
||||||
|
prelude::{App, IntoSystemDescriptor, Plugin},
|
||||||
|
};
|
||||||
|
use bevy_rapier3d::prelude::{NoUserData, RapierPhysicsPlugin};
|
||||||
|
|
||||||
|
mod components;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
|
pub use components::*;
|
||||||
|
use systems::*;
|
||||||
|
|
||||||
|
pub struct CyberActionPlugin;
|
||||||
|
impl Plugin for CyberActionPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.init_resource::<MovementSettings>()
|
||||||
|
.register_type::<MovementSettings>()
|
||||||
|
.init_resource::<CatControllerSettings>()
|
||||||
|
.init_resource::<ActionDebugInstant>()
|
||||||
|
.register_type::<CatControllerSettings>()
|
||||||
|
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default())
|
||||||
|
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||||
|
.add_startup_system(zero_gravity)
|
||||||
|
.add_system(surface_fix.label("surface_fix"))
|
||||||
|
.add_system(gravity.before("cat"))
|
||||||
|
.add_system(falling_cat.label("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"));
|
||||||
|
}
|
||||||
|
}
|
183
src/action/systems.rs
Normal file
183
src/action/systems.rs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
#[cfg(feature = "inspector")]
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use bevy::prelude::{
|
||||||
|
Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without,
|
||||||
|
};
|
||||||
|
use bevy_rapier3d::{
|
||||||
|
prelude::{
|
||||||
|
CollisionGroups, ExternalForce, Group, MultibodyJoint, QueryFilter, RapierConfiguration,
|
||||||
|
RapierContext, ReadMassProperties, Velocity,
|
||||||
|
},
|
||||||
|
rapier::prelude::JointAxis,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "inspector")]
|
||||||
|
use super::ActionDebugInstant;
|
||||||
|
use super::{CatControllerSettings, CatControllerState, MovementSettings, Tunneling};
|
||||||
|
use crate::{
|
||||||
|
bike::{CyberBikeBody, CyberSteering, CyberWheel, BIKE_WHEEL_COLLISION_GROUP},
|
||||||
|
input::InputState,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Disable gravity in Rapier.
|
||||||
|
pub(super) fn zero_gravity(mut config: ResMut<RapierConfiguration>) {
|
||||||
|
config.gravity = Vec3::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The gravity vector points from the cyberbike to the center of the planet.
|
||||||
|
pub(super) fn gravity(
|
||||||
|
mut query: Query<(&Transform, &mut ExternalForce, &ReadMassProperties), With<CyberBikeBody>>,
|
||||||
|
settings: Res<MovementSettings>,
|
||||||
|
) {
|
||||||
|
let (xform, mut forces, mprops) = query.single_mut();
|
||||||
|
let grav = xform.translation.normalize() * -settings.gravity * mprops.0.mass;
|
||||||
|
forces.force = grav;
|
||||||
|
forces.torque = Vec3::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
|
||||||
|
pub(super) fn falling_cat(
|
||||||
|
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>,
|
||||||
|
time: Res<Time>,
|
||||||
|
settings: Res<CatControllerSettings>,
|
||||||
|
#[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>,
|
||||||
|
) {
|
||||||
|
let (xform, mut forces, mut control_vars) = bike_query.single_mut();
|
||||||
|
let wup = xform.translation.normalize();
|
||||||
|
let bike_up = xform.up();
|
||||||
|
|
||||||
|
let wright = xform.forward().cross(wup).normalize();
|
||||||
|
|
||||||
|
let roll_error = wright.dot(bike_up);
|
||||||
|
let pitch_error = wup.dot(xform.back());
|
||||||
|
|
||||||
|
// only try to correct roll if we're not totally vertical
|
||||||
|
if pitch_error.abs() < 0.8 {
|
||||||
|
let (derivative, integral) = control_vars.update_roll(roll_error, time.delta_seconds());
|
||||||
|
let mag =
|
||||||
|
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
|
||||||
|
if mag.is_finite() {
|
||||||
|
forces.torque += xform.back() * mag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can do pitch correction any time, it's not coupled to roll
|
||||||
|
let (derivative, integral) = control_vars.update_pitch(pitch_error, time.delta_seconds());
|
||||||
|
let mag = (settings.kp * pitch_error) + (settings.ki * integral) + (settings.kd * derivative);
|
||||||
|
if mag.is_finite() {
|
||||||
|
forces.torque += wright * mag * 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "inspector")]
|
||||||
|
{
|
||||||
|
let now = Instant::now();
|
||||||
|
if (now - debug_instant.0).as_millis() > 500 {
|
||||||
|
dbg!(roll_error, pitch_error, &control_vars, mag);
|
||||||
|
debug_instant.0 = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
control_vars.decay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply forces to the cyberbike as a result of input.
|
||||||
|
pub(super) fn input_forces(
|
||||||
|
settings: Res<MovementSettings>,
|
||||||
|
input: Res<InputState>,
|
||||||
|
mut braking_query: Query<&mut MultibodyJoint, With<CyberWheel>>,
|
||||||
|
mut body_query: Query<
|
||||||
|
(&Transform, &mut ExternalForce),
|
||||||
|
(With<CyberBikeBody>, Without<CyberSteering>),
|
||||||
|
>,
|
||||||
|
mut steering_query: Query<&mut Transform, With<CyberSteering>>,
|
||||||
|
) {
|
||||||
|
let (xform, mut forces) = body_query.single_mut();
|
||||||
|
|
||||||
|
// thrust
|
||||||
|
let thrust = xform.forward() * input.throttle * settings.accel;
|
||||||
|
let point = xform.translation + xform.back();
|
||||||
|
let force = ExternalForce::at_point(thrust, point, xform.translation);
|
||||||
|
*forces += force;
|
||||||
|
|
||||||
|
// brake
|
||||||
|
for mut motor in braking_query.iter_mut() {
|
||||||
|
let factor = if input.brake { settings.accel } else { 0.0 };
|
||||||
|
motor.data.set_motor_velocity(JointAxis::X, 0.0, factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// steering
|
||||||
|
let angle = input.yaw * (PI / 2.0);
|
||||||
|
let mut steering = steering_query.single_mut();
|
||||||
|
steering.rotation = Quat::from_axis_angle(Vec3::Y, angle + 90.0f32.to_radians());
|
||||||
|
//dbg!(steering.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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: 6,
|
||||||
|
dir: -hit.1.normal,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn tunnel_out(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut wheel_query: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&mut Tunneling,
|
||||||
|
&mut ExternalForce,
|
||||||
|
&mut CollisionGroups,
|
||||||
|
),
|
||||||
|
With<CyberWheel>,
|
||||||
|
>,
|
||||||
|
mprops: Query<&ReadMassProperties, With<CyberBikeBody>>,
|
||||||
|
settings: Res<MovementSettings>,
|
||||||
|
) {
|
||||||
|
let mprops = mprops.single();
|
||||||
|
for (entity, mut tunneling, mut force, mut cgroups) in wheel_query.iter_mut() {
|
||||||
|
if tunneling.frames == 0 {
|
||||||
|
commands.entity(entity).remove::<Tunneling>();
|
||||||
|
force.force = Vec3::ZERO;
|
||||||
|
(cgroups.memberships, cgroups.filters) = BIKE_WHEEL_COLLISION_GROUP;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tunneling.frames -= 1;
|
||||||
|
force.force = tunneling.dir * settings.gravity * 1.5 * mprops.0.mass;
|
||||||
|
//#[cfg(feature = "inspector")]
|
||||||
|
dbg!(&tunneling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// General velocity-based drag-force calculation; does not take orientation
|
||||||
|
/// into account.
|
||||||
|
pub(super) fn drag(mut query: Query<(&Velocity, &mut ExternalForce), With<CyberBikeBody>>) {
|
||||||
|
let (vels, mut forces) = query.single_mut();
|
||||||
|
|
||||||
|
if let Some(vel) = vels.linvel.try_normalize() {
|
||||||
|
let v2 = vels.linvel.length_squared();
|
||||||
|
forces.force -= vel * (v2 * 0.02);
|
||||||
|
}
|
||||||
|
}
|
287
src/bike.rs
287
src/bike.rs
|
@ -1,287 +0,0 @@
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use bevy::prelude::{shape::UVSphere as Tire, *};
|
|
||||||
use bevy_rapier3d::{
|
|
||||||
geometry::Group,
|
|
||||||
prelude::{
|
|
||||||
Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction,
|
|
||||||
ImpulseJoint, PrismaticJointBuilder, Restitution, RigidBody, Sleeping,
|
|
||||||
TransformInterpolation, Velocity,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::planet::PLANET_RADIUS;
|
|
||||||
|
|
||||||
pub(crate) const SPAWN_ALTITUDE: f32 = PLANET_RADIUS * 0.2;
|
|
||||||
|
|
||||||
type Meshterial<'a> = (
|
|
||||||
ResMut<'a, Assets<Mesh>>,
|
|
||||||
ResMut<'a, Assets<StandardMaterial>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct CyberBikeBody;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct CyberBikeCollider;
|
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
|
||||||
pub struct CyberBikeModel;
|
|
||||||
|
|
||||||
#[derive(Debug, Component)]
|
|
||||||
pub struct CyberWheel;
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Default, Clone, Copy)]
|
|
||||||
pub struct CyberBikeControl {
|
|
||||||
pub error_sum: f32,
|
|
||||||
pub prev_error: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Reflect)]
|
|
||||||
#[reflect(Resource)]
|
|
||||||
pub struct WheelConfig {
|
|
||||||
pub front_forward: f32,
|
|
||||||
pub front_stance: f32,
|
|
||||||
pub rear_back: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub limits: [f32; 2],
|
|
||||||
pub stiffness: f32,
|
|
||||||
pub damping: f32,
|
|
||||||
pub radius: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for WheelConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
front_forward: 0.9,
|
|
||||||
front_stance: 0.65,
|
|
||||||
rear_back: 1.1,
|
|
||||||
y: -1.5,
|
|
||||||
limits: [-1.0, 1.0],
|
|
||||||
stiffness: 100.0,
|
|
||||||
damping: 1.0,
|
|
||||||
radius: 0.3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
fn spawn_cyberbike(
|
|
||||||
mut commands: Commands,
|
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
wheel_conf: Res<WheelConfig>,
|
|
||||||
mut meshterials: Meshterial,
|
|
||||||
) {
|
|
||||||
let xform = Transform::from_translation(Vec3::Y * SPAWN_ALTITUDE);
|
|
||||||
//.with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians()));
|
|
||||||
|
|
||||||
let damping = Damping {
|
|
||||||
angular_damping: 0.5,
|
|
||||||
linear_damping: 0.1,
|
|
||||||
};
|
|
||||||
let not_sleeping = Sleeping::disabled();
|
|
||||||
let ccd = Ccd { enabled: true };
|
|
||||||
|
|
||||||
let bcollider_shape =
|
|
||||||
Collider::capsule(Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, 0.0, 1.0), 0.50);
|
|
||||||
|
|
||||||
let friction = Friction {
|
|
||||||
coefficient: 0.0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let restitution = Restitution {
|
|
||||||
coefficient: 0.0,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mass_properties = ColliderMassProperties::Density(0.2);
|
|
||||||
|
|
||||||
let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
|
|
||||||
let bike_collision_group = CollisionGroups::new(membership, filter);
|
|
||||||
|
|
||||||
let scene = asset_server.load("cb-no-y_up.glb#Scene0");
|
|
||||||
|
|
||||||
let spatialbundle = SpatialBundle {
|
|
||||||
transform: xform,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let bike = commands
|
|
||||||
.spawn(RigidBody::Dynamic)
|
|
||||||
.insert(spatialbundle)
|
|
||||||
.insert((
|
|
||||||
bcollider_shape,
|
|
||||||
bike_collision_group,
|
|
||||||
mass_properties,
|
|
||||||
damping,
|
|
||||||
restitution,
|
|
||||||
friction,
|
|
||||||
not_sleeping,
|
|
||||||
ccd,
|
|
||||||
))
|
|
||||||
.insert(TransformInterpolation {
|
|
||||||
start: None,
|
|
||||||
end: None,
|
|
||||||
})
|
|
||||||
.insert(Velocity::zero())
|
|
||||||
.insert(ExternalForce::default())
|
|
||||||
.insert(CyberBikeCollider)
|
|
||||||
.with_children(|rider| {
|
|
||||||
rider.spawn(SceneBundle {
|
|
||||||
scene,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.insert(CyberBikeModel)
|
|
||||||
.insert(CyberBikeBody)
|
|
||||||
.insert(CyberBikeControl::default())
|
|
||||||
.id();
|
|
||||||
|
|
||||||
spawn_tires(&mut commands, &xform, bike, &wheel_conf, &mut meshterials);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn re_tire(
|
|
||||||
mut commands: Commands,
|
|
||||||
wheel_conf: ResMut<WheelConfig>,
|
|
||||||
mut meshterials: Meshterial,
|
|
||||||
bquery: Query<(Entity, &Transform), With<CyberBikeBody>>,
|
|
||||||
wheels: Query<Entity, With<CyberWheel>>,
|
|
||||||
) {
|
|
||||||
// we fuck with values in the egui inspector
|
|
||||||
let (bike, xform) = bquery.single();
|
|
||||||
if wheel_conf.is_changed() {
|
|
||||||
for wheel in wheels.iter() {
|
|
||||||
commands.entity(wheel).despawn_recursive();
|
|
||||||
}
|
|
||||||
spawn_tires(&mut commands, xform, bike, &wheel_conf, &mut meshterials);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_tires(
|
|
||||||
commands: &mut Commands,
|
|
||||||
xform: &Transform,
|
|
||||||
bike: Entity,
|
|
||||||
conf: &WheelConfig,
|
|
||||||
meshterials: &mut Meshterial,
|
|
||||||
) {
|
|
||||||
// re-set the collision group
|
|
||||||
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
|
|
||||||
let wheels_collision_group = CollisionGroups::new(membership, filter);
|
|
||||||
let wheel_y = conf.y;
|
|
||||||
let wheel_rad = conf.radius;
|
|
||||||
let stiffness = conf.stiffness;
|
|
||||||
let not_sleeping = Sleeping::disabled();
|
|
||||||
let ccd = Ccd { enabled: true };
|
|
||||||
let limits = conf.limits;
|
|
||||||
let (meshes, materials) = meshterials;
|
|
||||||
|
|
||||||
let tire = Tire {
|
|
||||||
radius: wheel_rad,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let material = StandardMaterial {
|
|
||||||
base_color: Color::Rgba {
|
|
||||||
red: 0.01,
|
|
||||||
green: 0.01,
|
|
||||||
blue: 0.01,
|
|
||||||
alpha: 1.0,
|
|
||||||
},
|
|
||||||
alpha_mode: AlphaMode::Opaque,
|
|
||||||
perceptual_roughness: 0.5,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let pbr_bundle = PbrBundle {
|
|
||||||
material: materials.add(material),
|
|
||||||
mesh: meshes.add(Mesh::from(tire)),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut wheel_poses = Vec::with_capacity(3);
|
|
||||||
|
|
||||||
// left front
|
|
||||||
{
|
|
||||||
let wheel_x = -conf.front_stance;
|
|
||||||
let wheel_z = -conf.front_forward;
|
|
||||||
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
|
|
||||||
wheel_poses.push(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// right front
|
|
||||||
{
|
|
||||||
let wheel_x = conf.front_stance;
|
|
||||||
let wheel_z = -conf.front_forward;
|
|
||||||
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
|
|
||||||
wheel_poses.push(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rear
|
|
||||||
{
|
|
||||||
let wheel_x = 0.0;
|
|
||||||
let wheel_z = conf.rear_back;
|
|
||||||
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
|
|
||||||
wheel_poses.push(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
for offset in wheel_poses {
|
|
||||||
let trans = xform.translation + offset;
|
|
||||||
let wheel_pos_in_world = Transform::from_rotation(xform.rotation).with_translation(trans);
|
|
||||||
let wheel_damping = Damping {
|
|
||||||
linear_damping: 0.8,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let wheel_collider = Collider::ball(wheel_rad);
|
|
||||||
let mass_props = ColliderMassProperties::Density(0.001);
|
|
||||||
|
|
||||||
let damping = conf.damping;
|
|
||||||
let prismatic = PrismaticJointBuilder::new(Vec3::Y)
|
|
||||||
.local_anchor1(offset)
|
|
||||||
.limits(limits)
|
|
||||||
.motor_position(0.0, stiffness, damping);
|
|
||||||
let joint = ImpulseJoint::new(bike, prismatic);
|
|
||||||
|
|
||||||
let spatial_bundle = SpatialBundle {
|
|
||||||
transform: wheel_pos_in_world,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let txform = wheel_pos_in_world.with_rotation(Quat::from_axis_angle(
|
|
||||||
wheel_pos_in_world.forward(),
|
|
||||||
90.0f32.to_radians(),
|
|
||||||
));
|
|
||||||
let tire_spundle = SpatialBundle {
|
|
||||||
transform: txform,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
commands
|
|
||||||
.spawn(RigidBody::Dynamic)
|
|
||||||
.insert(spatial_bundle)
|
|
||||||
.insert((
|
|
||||||
wheel_collider,
|
|
||||||
mass_props,
|
|
||||||
wheel_damping,
|
|
||||||
ccd,
|
|
||||||
not_sleeping,
|
|
||||||
joint,
|
|
||||||
wheels_collision_group,
|
|
||||||
))
|
|
||||||
.with_children(|wheel| {
|
|
||||||
wheel.spawn(tire_spundle).insert(pbr_bundle.clone());
|
|
||||||
})
|
|
||||||
.insert(CyberWheel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CyberBikePlugin;
|
|
||||||
impl Plugin for CyberBikePlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.insert_resource(WheelConfig::default())
|
|
||||||
.register_type::<WheelConfig>()
|
|
||||||
.add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike)
|
|
||||||
.add_system(re_tire);
|
|
||||||
}
|
|
||||||
}
|
|
81
src/bike/body.rs
Normal file
81
src/bike/body.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use bevy::{
|
||||||
|
prelude::{AssetServer, BuildChildren, Commands, Quat, Res, SpatialBundle, Transform, Vec3},
|
||||||
|
scene::SceneBundle,
|
||||||
|
};
|
||||||
|
use bevy_rapier3d::prelude::{
|
||||||
|
Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction,
|
||||||
|
ReadMassProperties, Restitution, RigidBody, Sleeping, TransformInterpolation, Velocity,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{spawn_tires, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP};
|
||||||
|
use crate::{action::CatControllerState, planet::PLANET_RADIUS};
|
||||||
|
|
||||||
|
pub(super) fn spawn_cyberbike(
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
wheel_conf: Res<WheelConfig>,
|
||||||
|
mut meshterials: Meshterial,
|
||||||
|
) {
|
||||||
|
let altitude = 40.0;
|
||||||
|
|
||||||
|
let xform = Transform::from_translation(Vec3::X * 10.0 + Vec3::Y * altitude);
|
||||||
|
|
||||||
|
let damping = Damping {
|
||||||
|
angular_damping: 2.0,
|
||||||
|
linear_damping: 0.1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let friction = Friction {
|
||||||
|
coefficient: 0.3,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let restitution = Restitution {
|
||||||
|
coefficient: 0.0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mass_properties = ColliderMassProperties::Density(0.9);
|
||||||
|
|
||||||
|
let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
|
||||||
|
let bike_collision_group = CollisionGroups::new(membership, filter);
|
||||||
|
|
||||||
|
let scene = asset_server.load("cb-no-y_up.glb#Scene0");
|
||||||
|
|
||||||
|
let spatialbundle = SpatialBundle {
|
||||||
|
transform: xform,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let bike = commands
|
||||||
|
.spawn(RigidBody::Dynamic)
|
||||||
|
.insert(spatialbundle)
|
||||||
|
.insert((
|
||||||
|
Collider::capsule(Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, 0.0, 1.0), 0.50),
|
||||||
|
bike_collision_group,
|
||||||
|
mass_properties,
|
||||||
|
damping,
|
||||||
|
restitution,
|
||||||
|
friction,
|
||||||
|
Sleeping::disabled(),
|
||||||
|
Ccd { enabled: true },
|
||||||
|
ReadMassProperties::default(),
|
||||||
|
))
|
||||||
|
.insert(TransformInterpolation {
|
||||||
|
start: None,
|
||||||
|
end: None,
|
||||||
|
})
|
||||||
|
.insert(Velocity::zero())
|
||||||
|
.insert(ExternalForce::default())
|
||||||
|
.with_children(|rider| {
|
||||||
|
rider.spawn(SceneBundle {
|
||||||
|
scene,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.insert(CyberBikeBody)
|
||||||
|
.insert(CatControllerState::default())
|
||||||
|
.id();
|
||||||
|
|
||||||
|
spawn_tires(&mut commands, bike, xform, &wheel_conf, &mut meshterials);
|
||||||
|
}
|
41
src/bike/components.rs
Normal file
41
src/bike/components.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use bevy::{
|
||||||
|
prelude::{Component, ReflectResource, Resource},
|
||||||
|
reflect::Reflect,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct CyberBikeBody;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct CyberSteering;
|
||||||
|
|
||||||
|
#[derive(Debug, Component)]
|
||||||
|
pub struct CyberWheel;
|
||||||
|
|
||||||
|
#[derive(Resource, Reflect)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct WheelConfig {
|
||||||
|
pub front_forward: f32,
|
||||||
|
pub front_stance: f32,
|
||||||
|
pub rear_back: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub limits: [f32; 2],
|
||||||
|
pub stiffness: f32,
|
||||||
|
pub damping: f32,
|
||||||
|
pub radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WheelConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
front_forward: 0.9,
|
||||||
|
front_stance: 0.65,
|
||||||
|
rear_back: 1.1,
|
||||||
|
y: -0.45,
|
||||||
|
limits: [-0.7, 0.1],
|
||||||
|
stiffness: 90.0,
|
||||||
|
damping: 8.0,
|
||||||
|
radius: 0.3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/bike/mod.rs
Normal file
26
src/bike/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
mod body;
|
||||||
|
mod components;
|
||||||
|
mod wheels;
|
||||||
|
|
||||||
|
use bevy::prelude::{App, Assets, Mesh, Plugin, ResMut, StandardMaterial, StartupStage};
|
||||||
|
use bevy_rapier3d::prelude::Group;
|
||||||
|
|
||||||
|
pub(crate) use self::components::*;
|
||||||
|
use self::{body::spawn_cyberbike, wheels::spawn_tires};
|
||||||
|
|
||||||
|
pub const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1);
|
||||||
|
pub const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10);
|
||||||
|
|
||||||
|
type Meshterial<'a> = (
|
||||||
|
ResMut<'a, Assets<Mesh>>,
|
||||||
|
ResMut<'a, Assets<StandardMaterial>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct CyberBikePlugin;
|
||||||
|
impl Plugin for CyberBikePlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(WheelConfig::default())
|
||||||
|
.register_type::<WheelConfig>()
|
||||||
|
.add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike);
|
||||||
|
}
|
||||||
|
}
|
157
src/bike/wheels.rs
Normal file
157
src/bike/wheels.rs
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
use bevy::prelude::{
|
||||||
|
shape::Capsule as Tire, AlphaMode, BuildChildren, Color, Commands, Entity, Mesh, PbrBundle,
|
||||||
|
Quat, SpatialBundle, StandardMaterial, Transform, Vec3,
|
||||||
|
};
|
||||||
|
use bevy_rapier3d::prelude::{
|
||||||
|
Ccd, CoefficientCombineRule, Collider, ColliderMassProperties, CollisionGroups, Damping,
|
||||||
|
ExternalForce, Friction, LockedAxes, MultibodyJoint, PrismaticJointBuilder, Restitution,
|
||||||
|
RevoluteJointBuilder, RigidBody, Sleeping, TransformInterpolation,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP};
|
||||||
|
|
||||||
|
pub fn spawn_tires(
|
||||||
|
commands: &mut Commands,
|
||||||
|
bike: Entity,
|
||||||
|
xform: Transform,
|
||||||
|
conf: &WheelConfig,
|
||||||
|
meshterials: &mut Meshterial,
|
||||||
|
) {
|
||||||
|
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
|
||||||
|
let wheels_collision_group = CollisionGroups::new(membership, filter);
|
||||||
|
let wheel_y = conf.y;
|
||||||
|
let wheel_rad = conf.radius;
|
||||||
|
let stiffness = conf.stiffness;
|
||||||
|
let not_sleeping = Sleeping::disabled();
|
||||||
|
let ccd = Ccd { enabled: true };
|
||||||
|
let limits = conf.limits;
|
||||||
|
let (meshes, materials) = meshterials;
|
||||||
|
|
||||||
|
let tire = Tire {
|
||||||
|
radius: wheel_rad,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let material = StandardMaterial {
|
||||||
|
base_color: Color::Rgba {
|
||||||
|
red: 0.01,
|
||||||
|
green: 0.01,
|
||||||
|
blue: 0.01,
|
||||||
|
alpha: 1.0,
|
||||||
|
},
|
||||||
|
alpha_mode: AlphaMode::Opaque,
|
||||||
|
perceptual_roughness: 0.5,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let pbr_bundle = PbrBundle {
|
||||||
|
material: materials.add(material),
|
||||||
|
mesh: meshes.add(Mesh::from(tire)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let friction = Friction {
|
||||||
|
coefficient: 1.0,
|
||||||
|
combine_rule: CoefficientCombineRule::Min,
|
||||||
|
};
|
||||||
|
|
||||||
|
let restitution = Restitution {
|
||||||
|
coefficient: 0.8,
|
||||||
|
combine_rule: CoefficientCombineRule::Average,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut wheel_poses = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
// front
|
||||||
|
{
|
||||||
|
let wheel_x = 0.0;
|
||||||
|
let wheel_z = -conf.front_forward;
|
||||||
|
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
|
||||||
|
wheel_poses.push((offset, Some(CyberSteering)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// rear
|
||||||
|
{
|
||||||
|
let wheel_x = 0.0;
|
||||||
|
let wheel_z = conf.rear_back;
|
||||||
|
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
|
||||||
|
wheel_poses.push((offset, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (offset, steering) in wheel_poses {
|
||||||
|
let wheel_damping = Damping {
|
||||||
|
linear_damping: 0.8,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let wheel_collider = Collider::capsule(-Vec3::Y, Vec3::Y, 0.3);
|
||||||
|
let mass_props = ColliderMassProperties::Density(0.1);
|
||||||
|
|
||||||
|
let damping = conf.damping;
|
||||||
|
let prismatic = PrismaticJointBuilder::new(Vec3::Y)
|
||||||
|
.local_anchor1(offset)
|
||||||
|
.limits(limits)
|
||||||
|
.motor_position(limits[0], stiffness, damping);
|
||||||
|
|
||||||
|
let fork = commands
|
||||||
|
.spawn(SpatialBundle {
|
||||||
|
transform: Transform::from_translation(offset),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.set_parent(bike)
|
||||||
|
.id();
|
||||||
|
|
||||||
|
let suspension = MultibodyJoint::new(fork, prismatic);
|
||||||
|
let sentity = commands
|
||||||
|
.spawn(RigidBody::Dynamic)
|
||||||
|
.insert(suspension)
|
||||||
|
.insert(SpatialBundle {
|
||||||
|
transform: Transform::from_translation(offset),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.set_parent(bike)
|
||||||
|
.id();
|
||||||
|
|
||||||
|
if let Some(steering) = steering {
|
||||||
|
commands.entity(sentity).insert(steering);
|
||||||
|
}
|
||||||
|
|
||||||
|
let revolute = RevoluteJointBuilder::new(Vec3::Y);
|
||||||
|
let axel = MultibodyJoint::new(sentity, revolute);
|
||||||
|
let wheel_bundle = (
|
||||||
|
wheel_collider,
|
||||||
|
mass_props,
|
||||||
|
wheel_damping,
|
||||||
|
ccd,
|
||||||
|
wheels_collision_group,
|
||||||
|
friction,
|
||||||
|
restitution,
|
||||||
|
CyberWheel,
|
||||||
|
ExternalForce::default(),
|
||||||
|
not_sleeping,
|
||||||
|
//axel,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _wentity = commands
|
||||||
|
.spawn(RigidBody::Dynamic)
|
||||||
|
.insert(SpatialBundle {
|
||||||
|
// transform: Transform::from_rotation(xform.rotation)
|
||||||
|
// .with_translation(xform.translation)
|
||||||
|
transform: Transform::from_rotation(Quat::from_axis_angle(
|
||||||
|
Vec3::Z,
|
||||||
|
90.0f32.to_radians(),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(wheel_bundle)
|
||||||
|
.with_children(|wheel| {
|
||||||
|
wheel
|
||||||
|
.spawn(SpatialBundle::default())
|
||||||
|
.insert(pbr_bundle.clone())
|
||||||
|
.insert(TransformInterpolation {
|
||||||
|
start: None,
|
||||||
|
end: None,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.set_parent(sentity)
|
||||||
|
.id();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::{bike::CyberBikeModel, input::InputState};
|
use crate::{bike::CyberBikeBody, input::InputState};
|
||||||
|
|
||||||
// 85 degrees in radians
|
// 85 degrees in radians
|
||||||
const MAX_PITCH: f32 = 1.48353;
|
const MAX_PITCH: f32 = 1.48353;
|
||||||
|
@ -57,7 +57,7 @@ fn setup_cybercams(mut commands: Commands) {
|
||||||
fn follow_cyberbike(
|
fn follow_cyberbike(
|
||||||
mut query: ParamSet<(
|
mut query: ParamSet<(
|
||||||
// 0: the bike
|
// 0: the bike
|
||||||
Query<&Transform, With<CyberBikeModel>>,
|
Query<&Transform, With<CyberBikeBody>>,
|
||||||
// 1: the cameras
|
// 1: the cameras
|
||||||
Query<(&mut Transform, &CyberCameras)>,
|
Query<(&mut Transform, &CyberCameras)>,
|
||||||
)>,
|
)>,
|
||||||
|
@ -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);
|
||||||
|
@ -119,32 +119,6 @@ fn cycle_cam_state(mut state: ResMut<State<CyberCameras>>, mut keys: ResMut<Inpu
|
||||||
pub struct CyberCamPlugin;
|
pub struct CyberCamPlugin;
|
||||||
|
|
||||||
impl Plugin for CyberCamPlugin {
|
impl Plugin for CyberCamPlugin {
|
||||||
#[cfg(feature = "inspector")]
|
|
||||||
fn build(&self, app: &mut bevy::prelude::App) {
|
|
||||||
use bevy_rapier3d::render::{DebugRenderMode, DebugRenderStyle, RapierDebugRenderPlugin};
|
|
||||||
let style = DebugRenderStyle {
|
|
||||||
collider_dynamic_color: Color::RED.as_rgba_f32(),
|
|
||||||
multibody_joint_anchor_color: Color::GREEN.as_rgba_f32(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let mode = DebugRenderMode::COLLIDER_SHAPES
|
|
||||||
| DebugRenderMode::CONTACTS
|
|
||||||
| DebugRenderMode::SOLVER_CONTACTS
|
|
||||||
| DebugRenderMode::JOINTS
|
|
||||||
| DebugRenderMode::RIGID_BODY_AXES;
|
|
||||||
|
|
||||||
let rplugin = RapierDebugRenderPlugin {
|
|
||||||
style,
|
|
||||||
always_on_top: true,
|
|
||||||
enabled: true,
|
|
||||||
mode,
|
|
||||||
};
|
|
||||||
|
|
||||||
app.add_plugin(rplugin);
|
|
||||||
common(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "inspector"))]
|
|
||||||
fn build(&self, app: &mut bevy::prelude::App) {
|
fn build(&self, app: &mut bevy::prelude::App) {
|
||||||
common(app);
|
common(app);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{lights::AnimateCyberLightWireframe, planet::CyberPlanet};
|
||||||
|
|
||||||
pub const BISEXY_COLOR: Color = Color::hsla(292.0, 0.9, 0.60, 1.1);
|
pub const BISEXY_COLOR: Color = Color::hsla(292.0, 0.9, 0.60, 1.1);
|
||||||
|
|
||||||
fn wireframe_planet(
|
fn _wireframe_planet(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut polylines: ResMut<Assets<Polyline>>,
|
mut polylines: ResMut<Assets<Polyline>>,
|
||||||
|
@ -57,7 +57,7 @@ fn wireframe_planet(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wireframify_lights(mut lights: Query<&mut AnimateCyberLightWireframe>) {
|
fn _wireframify_lights(mut lights: Query<&mut AnimateCyberLightWireframe>) {
|
||||||
let chance = 0.005;
|
let chance = 0.005;
|
||||||
|
|
||||||
let rng = &mut thread_rng();
|
let rng = &mut thread_rng();
|
||||||
|
@ -74,9 +74,30 @@ fn wireframify_lights(mut lights: Query<&mut AnimateCyberLightWireframe>) {
|
||||||
pub struct CyberGlamorPlugin;
|
pub struct CyberGlamorPlugin;
|
||||||
impl Plugin for CyberGlamorPlugin {
|
impl Plugin for CyberGlamorPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(ClearColor(Color::rgb(0.408, 0.6236, 0.925)))
|
app.insert_resource(ClearColor(Color::rgb(0.408, 0.6236, 0.925)));
|
||||||
.add_startup_system_to_stage(StartupStage::PostStartup, wireframe_planet)
|
|
||||||
.add_system(wireframify_lights)
|
{
|
||||||
.add_plugin(PolylinePlugin);
|
use bevy_rapier3d::render::{
|
||||||
|
DebugRenderMode, DebugRenderStyle, RapierDebugRenderPlugin,
|
||||||
|
};
|
||||||
|
let style = DebugRenderStyle {
|
||||||
|
multibody_joint_anchor_color: Color::GREEN.as_rgba_f32(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mode = DebugRenderMode::CONTACTS
|
||||||
|
| DebugRenderMode::SOLVER_CONTACTS
|
||||||
|
| DebugRenderMode::COLLIDER_SHAPES
|
||||||
|
| DebugRenderMode::JOINTS
|
||||||
|
| DebugRenderMode::RIGID_BODY_AXES;
|
||||||
|
|
||||||
|
let rplugin = RapierDebugRenderPlugin {
|
||||||
|
style,
|
||||||
|
always_on_top: true,
|
||||||
|
enabled: true,
|
||||||
|
mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
app.add_plugin(rplugin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -1,20 +1,8 @@
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use cyber_rider::{
|
use cyber_rider::{
|
||||||
action::{CyberActionPlugin, MovementSettings},
|
action::CyberActionPlugin, bike::CyberBikePlugin, camera::CyberCamPlugin, disable_mouse_trap,
|
||||||
bike::CyberBikePlugin,
|
glamor::CyberGlamorPlugin, input::CyberInputPlugin, lights::CyberSpaceLightsPlugin,
|
||||||
camera::CyberCamPlugin,
|
planet::CyberPlanetPlugin, ui::CyberUIPlugin,
|
||||||
disable_mouse_trap,
|
|
||||||
glamor::CyberGlamorPlugin,
|
|
||||||
input::CyberInputPlugin,
|
|
||||||
lights::CyberSpaceLightsPlugin,
|
|
||||||
planet::CyberPlanetPlugin,
|
|
||||||
ui::CyberUIPlugin,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MOVEMENT_SETTINGS: MovementSettings = MovementSettings {
|
|
||||||
sensitivity: 8.0, // steering
|
|
||||||
accel: 30.0, // thrust
|
|
||||||
gravity: 9.0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -28,7 +16,6 @@ fn main() {
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}))
|
}))
|
||||||
.insert_resource(MOVEMENT_SETTINGS)
|
|
||||||
.add_plugin(CyberPlanetPlugin)
|
.add_plugin(CyberPlanetPlugin)
|
||||||
.add_plugin(CyberGlamorPlugin)
|
.add_plugin(CyberGlamorPlugin)
|
||||||
.add_plugin(CyberInputPlugin)
|
.add_plugin(CyberInputPlugin)
|
||||||
|
|
Loading…
Reference in a new issue