Compare commits

...

2 commits

Author SHA1 Message Date
Joe Ardent
3d0d86f24c compiles and runs, but crashes 2024-07-15 17:30:26 -07:00
Joe Ardent
4d94f2bfa4 update deps for switching to Avian3d 2024-07-15 17:30:26 -07:00
16 changed files with 2516 additions and 1560 deletions

3343
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,32 +6,22 @@ edition = "2021"
[dependencies] [dependencies]
rand = "0.8" rand = "0.8"
# bevy_polyline = "0.4" # bevy_polyline = "0.4"
noise = { git = "https://github.com/Razaekel/noise-rs" } noise = "0.9"
hexasphere = "8" hexasphere = "14"
wgpu = "0.15" wgpu = "0.20"
bevy-inspector-egui = "0.18" # bevy-inspector-egui = "0.18"
[features] [features]
inspector = [] inspector = []
[dependencies.bevy] [dependencies.bevy]
version = "0.10" version = "0.14"
default-features = false default-features = true
features = [ features = ["bevy_dev_tools"]
"bevy_gilrs",
"bevy_winit",
"png",
"hdr",
"x11",
"bevy_ui",
"bevy_text",
"bevy_gltf",
"bevy_sprite",
]
[dependencies.bevy_rapier3d] [dependencies.avian3d]
features = ["debug-render-3d"] default-features = true
version = "0.21" version = "0.1"
# Maybe also enable only a small amount of optimization for our code: # Maybe also enable only a small amount of optimization for our code:
[profile.dev] [profile.dev]

View file

@ -1,7 +1,7 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use bevy::{ use bevy::{
prelude::{Component, ReflectResource, Resource, Vec3}, prelude::{Component, ReflectResource, Resource},
reflect::Reflect, reflect::Reflect,
}; };
@ -26,21 +26,6 @@ impl 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 {

View file

@ -1,10 +1,11 @@
use avian3d::prelude::{PhysicsPlugins, SubstepCount};
use bevy::{ use bevy::{
app::Update,
diagnostic::FrameTimeDiagnosticsPlugin, diagnostic::FrameTimeDiagnosticsPlugin,
ecs::reflect::ReflectResource, ecs::reflect::ReflectResource,
prelude::{App, IntoSystemConfigs, Plugin, Resource}, prelude::{App, IntoSystemConfigs, Plugin, Resource},
reflect::Reflect, reflect::Reflect,
}; };
use bevy_rapier3d::prelude::{NoUserData, RapierPhysicsPlugin};
mod components; mod components;
mod systems; mod systems;
@ -28,20 +29,14 @@ impl Plugin for CyberActionPlugin {
.init_resource::<CyberLean>() .init_resource::<CyberLean>()
.register_type::<CyberLean>() .register_type::<CyberLean>()
.register_type::<CatControllerSettings>() .register_type::<CatControllerSettings>()
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default()) .add_plugins((PhysicsPlugins::default(), FrameTimeDiagnosticsPlugin))
.add_startup_system(timestep_setup) .insert_resource(SubstepCount(12))
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_systems( .add_systems(
Update,
( (
gravity, gravity,
cyber_lean, (cyber_lean, suspension, falling_cat, input_forces).after(clear_forces),
falling_cat, ),
input_forces,
drag,
tunnel_out,
surface_fix,
)
.chain(),
); );
} }
} }

View file

@ -1,18 +1,17 @@
use std::f32::consts::{FRAC_PI_3, FRAC_PI_4}; use std::f32::consts::{FRAC_PI_3, FRAC_PI_4};
use bevy::prelude::{ use avian3d::{
Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without, dynamics::solver::xpbd::AngularConstraint,
}; prelude::{
use bevy_rapier3d::prelude::{ ColliderMassProperties, ExternalForce, ExternalTorque, Gravity, LinearVelocity,
CollisionGroups, ExternalForce, Group, MultibodyJoint, QueryFilter, RapierConfiguration, PrismaticJoint, RigidBodyQuery,
RapierContext, ReadMassProperties, TimestepMode, Velocity, },
}; };
use bevy::prelude::{Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without};
#[cfg(feature = "inspector")] use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings};
use super::ActionDebugInstant;
use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings, Tunneling};
use crate::{ use crate::{
bike::{CyberBikeBody, CyberSteering, CyberWheel, WheelConfig, BIKE_WHEEL_COLLISION_GROUP}, bike::{CyberBikeBody, CyberFork, CyberSpring, CyberSteering, CyberWheel, WheelConfig},
input::InputState, input::InputState,
}; };
@ -36,46 +35,56 @@ fn rotate_point(pt: &Vec3, rot: &Quat) -> Vec3 {
-Vec3::from_array([rot_qpt.x, rot_qpt.y, rot_qpt.z]) -Vec3::from_array([rot_qpt.x, rot_qpt.y, rot_qpt.z])
} }
pub(super) fn timestep_setup(mut config: ResMut<RapierConfiguration>) {
let ts = TimestepMode::Fixed {
dt: 1.0 / 60.0,
substeps: 2,
};
config.timestep_mode = ts;
}
/// 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(super) fn gravity( pub(super) fn gravity(
mut query: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>, xform: Query<&Transform, With<CyberBikeBody>>,
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
mut rapier_config: ResMut<RapierConfiguration>, mut gravity: ResMut<Gravity>,
#[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>,
) { ) {
let (xform, mut forces) = query.single_mut(); let xform = xform.single();
*gravity = Gravity(xform.translation.normalize() * -settings.gravity);
#[cfg(feature = "inspectorb")] // clear all external forces
{ }
if debug_instant.elapsed().as_millis() > 6000 {
dbg!(&forces); pub(super) fn clear_forces(mut forces: Query<(&mut ExternalForce, &mut ExternalTorque)>) {
debug_instant.reset(); for (mut force, mut torque) in forces.iter_mut() {
force.clear();
torque.clear();
} }
} }
rapier_config.gravity = xform.translation.normalize() * -settings.gravity; pub(super) fn suspension(
forces.force = Vec3::ZERO; movment_settings: Res<MovementSettings>,
forces.torque = Vec3::ZERO; wheel_config: Res<WheelConfig>,
mass: Query<&ColliderMassProperties, With<CyberBikeBody>>,
mut axels: Query<(&mut ExternalForce, &CyberSpring, &Transform)>,
) {
let mass = if let Ok(mass) = mass.get_single() {
mass.mass.0
} else {
1.0
};
let gravity = movment_settings.gravity;
let mag = -wheel_config.stiffness * mass * gravity;
for (mut force, spring, xform) in axels.iter_mut() {
let spring = mag * spring.0;
let spring = xform.translation + spring;
let _ = force.apply_force(spring);
}
} }
/// The desired lean angle, given steering input and speed. /// The desired lean angle, given steering input and speed.
pub(super) fn cyber_lean( pub(super) fn cyber_lean(
bike_state: Query<(&Velocity, &Transform), With<CyberBikeBody>>, bike_state: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
wheels: Query<&Transform, With<CyberWheel>>, wheels: Query<&Transform, With<CyberWheel>>,
input: Res<InputState>, input: Res<InputState>,
gravity_settings: Res<MovementSettings>, gravity_settings: Res<MovementSettings>,
mut lean: ResMut<CyberLean>, mut lean: ResMut<CyberLean>,
) { ) {
let (velocity, xform) = bike_state.single(); let (velocity, xform) = bike_state.single();
let vel = velocity.linvel.dot(xform.forward()); let vel = velocity.dot(*xform.forward());
let v_squared = vel.powi(2); let v_squared = vel.powi(2);
let steering_angle = yaw_to_angle(input.yaw); let steering_angle = yaw_to_angle(input.yaw);
let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect(); let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect();
@ -94,20 +103,20 @@ pub(super) fn cyber_lean(
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright. /// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(super) fn falling_cat( pub(super) fn falling_cat(
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>, mut bike_query: Query<(&Transform, &mut ExternalTorque, &mut CatControllerState)>,
time: Res<Time>, time: Res<Time>,
settings: Res<CatControllerSettings>, settings: Res<CatControllerSettings>,
lean: Res<CyberLean>, lean: Res<CyberLean>,
) { ) {
let (xform, mut forces, mut control_vars) = bike_query.single_mut(); let (xform, mut torque, mut control_vars) = bike_query.single_mut();
let world_up = xform.translation.normalize(); let world_up = xform.translation.normalize();
let rot = Quat::from_axis_angle(xform.back(), lean.lean); let rot = Quat::from_axis_angle(*xform.back(), lean.lean);
let target_up = rotate_point(&world_up, &rot).normalize(); let target_up = rotate_point(&world_up, &rot).normalize();
let bike_right = xform.right(); let bike_right = xform.right();
let roll_error = bike_right.dot(target_up); let roll_error = bike_right.dot(target_up);
let pitch_error = world_up.dot(xform.back()); let pitch_error = world_up.dot(*xform.back());
// only try to correct roll if we're not totally vertical // only try to correct roll if we're not totally vertical
if pitch_error.abs() < 0.95 { if pitch_error.abs() < 0.95 {
@ -115,7 +124,7 @@ pub(super) fn falling_cat(
let mag = let mag =
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative); (settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() { if mag.is_finite() {
forces.torque += xform.back() * mag; torque.apply_torque(*xform.back() * mag);
} }
} }
} }
@ -124,20 +133,31 @@ pub(super) fn falling_cat(
pub(super) fn input_forces( pub(super) fn input_forces(
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
input: Res<InputState>, input: Res<InputState>,
mut braking_query: Query<&mut MultibodyJoint, (Without<CyberSteering>, With<CyberWheel>)>, time: Res<Time>,
fork: Query<&PrismaticJoint, With<CyberFork>>,
mut axle: Query<(RigidBodyQuery, &Transform), With<CyberSteering>>,
mut braking_query: Query<&mut ExternalTorque, With<CyberWheel>>,
mut body_query: Query< mut body_query: Query<
(&Transform, &mut ExternalForce), (
RigidBodyQuery,
&Transform,
&ColliderMassProperties,
&mut ExternalForce,
),
(With<CyberBikeBody>, Without<CyberSteering>), (With<CyberBikeBody>, Without<CyberSteering>),
>, >,
mut steering_query: Query<&mut MultibodyJoint, With<CyberSteering>>,
) { ) {
let (xform, mut forces) = body_query.single_mut(); let Ok((mut bike, xform, mass, mut forces)) = body_query.get_single_mut() else {
bevy::log::warn!("no bike body found");
return;
};
let dt = time.delta_seconds();
// thrust // thrust
let thrust = xform.forward() * input.throttle * settings.accel; let thrust = xform.forward() * input.throttle * settings.accel;
let point = xform.translation + xform.back(); let point = xform.translation + *xform.back();
let force = ExternalForce::at_point(thrust, point, xform.translation);
*forces += force; let _ = *forces.apply_force_at_point(dt * thrust, point, mass.center_of_mass.0);
// brake + thrust // brake + thrust
for mut motor in braking_query.iter_mut() { for mut motor in braking_query.iter_mut() {
@ -147,99 +167,21 @@ pub(super) fn input_forces(
input.throttle * settings.accel input.throttle * settings.accel
}; };
let speed = if input.brake { 0.0 } else { -70.0 }; let speed = if input.brake { 0.0 } else { -70.0 };
motor.data = (*motor let target = dt * factor * speed;
.data let torque = target * Vec3::X;
.as_revolute_mut() motor.apply_torque(torque);
.unwrap()
.set_motor_max_force(factor)
.set_motor_velocity(speed, factor))
.into();
} }
// steering // steering
let angle = yaw_to_angle(input.yaw); let _angle = yaw_to_angle(input.yaw);
let mut steering = steering_query.single_mut(); let (mut axle, xform) = axle.single_mut();
steering.data = (*steering
.data
.as_revolute_mut()
.unwrap()
.set_motor_position(-angle, 100.0, 0.5))
.into();
}
/// Don't let the wheels get stuck underneat the planet let cur_rot = xform.rotation;
pub(super) fn surface_fix( let fork = fork.single();
mut commands: Commands, let axis = fork.free_axis.normalize();
mut wheel_query: Query< let new = Quat::from_axis_angle(axis, _angle);
(Entity, &Transform, &mut CollisionGroups), let diff = (new - cur_rot).to_scaled_axis();
(With<CyberWheel>, Without<Tunneling>), let mut compliance = 1.0;
>,
span_query: Query<&Transform, With<CyberWheel>>,
config: Res<WheelConfig>,
context: Res<RapierContext>,
) {
let mut wheels = Vec::new();
for xform in span_query.iter() {
wheels.push(xform);
}
let span = (wheels[1].translation - wheels[0].translation).normalize();
for (entity, xform, mut cgroups) in wheel_query.iter_mut() { fork.align_orientation(&mut axle, &mut bike, diff, &mut compliance, 1.0, dt);
//let ray_dir = xform.translation.normalize();
let ray_dir = xform.right().cross(span).normalize();
if let Some(hit) = context.cast_ray_and_get_normal(
xform.translation,
ray_dir,
config.radius * 1.1,
false,
QueryFilter::only_fixed(),
) {
cgroups.memberships = Group::NONE;
cgroups.filters = Group::NONE;
commands.entity(entity).insert(Tunneling {
frames: 3,
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);
}
} }

View file

@ -1,11 +1,8 @@
use avian3d::prelude::*;
use bevy::{ use bevy::{
prelude::{AssetServer, BuildChildren, Commands, Quat, Res, SpatialBundle, Transform, Vec3}, prelude::{AssetServer, BuildChildren, Commands, Quat, Res, SpatialBundle, Transform, Vec3},
scene::SceneBundle, scene::SceneBundle,
}; };
use bevy_rapier3d::prelude::{
Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction,
ReadMassProperties, Restitution, RigidBody, Sleeping, TransformInterpolation, Velocity,
};
use super::{spawn_wheels, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP}; use super::{spawn_wheels, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP};
use crate::{action::CatControllerState, planet::PLANET_RADIUS}; use crate::{action::CatControllerState, planet::PLANET_RADIUS};
@ -24,14 +21,10 @@ pub(super) fn spawn_cyberbike(
let right = xform.right() * 350.0; let right = xform.right() * 350.0;
xform.translation += right; xform.translation += right;
let damping = Damping {
angular_damping: 2.0,
linear_damping: 0.1,
};
let friction = Friction { let friction = Friction {
coefficient: 0.01, dynamic_coefficient: 0.1,
..Default::default() static_coefficient: 0.6,
combine_rule: CoefficientCombine::Average,
}; };
let restitution = Restitution { let restitution = Restitution {
@ -39,46 +32,38 @@ pub(super) fn spawn_cyberbike(
..Default::default() ..Default::default()
}; };
let mass_properties = ColliderMassProperties::Density(1.5);
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 = CollisionLayers::new(membership, filter);
let scene = asset_server.load("cb-no-y_up.glb#Scene0"); let scene = asset_server.load("cb-no-y_up.glb#Scene0");
let spatialbundle = SpatialBundle { let body_collider =
transform: xform, Collider::capsule_endpoints(0.5, Vec3::new(0.0, 0.0, -0.65), Vec3::new(0.0, 0.0, 0.8));
..Default::default()
};
let bike = commands let bike = commands
.spawn(RigidBody::Dynamic) .spawn(SpatialBundle::from_transform(xform))
.insert(spatialbundle)
.insert(( .insert((
Collider::capsule(Vec3::new(0.0, 0.0, -0.65), Vec3::new(0.0, 0.0, 0.8), 0.5), RigidBody::Dynamic,
body_collider,
bike_collision_group, bike_collision_group,
mass_properties,
damping,
restitution, restitution,
friction, friction,
Sleeping::disabled(), SleepingDisabled,
Ccd { enabled: true }, CyberBikeBody,
ReadMassProperties::default(), CatControllerState::default(),
ColliderDensity(0.6),
LinearDamping(0.1),
AngularDamping(2.0),
LinearVelocity::ZERO,
AngularVelocity::ZERO,
ExternalForce::ZERO,
ExternalTorque::ZERO,
)) ))
.insert(TransformInterpolation {
start: None,
end: None,
})
.insert(Velocity::zero())
.insert(ExternalForce::default())
.with_children(|rider| { .with_children(|rider| {
rider.spawn(SceneBundle { rider.spawn(SceneBundle {
scene, scene,
..Default::default() ..Default::default()
}); });
}) })
.insert(CyberBikeBody)
.insert(CatControllerState::default())
.id(); .id();
spawn_wheels(&mut commands, bike, &wheel_conf, &mut meshterials); spawn_wheels(&mut commands, bike, &wheel_conf, &mut meshterials);

View file

@ -1,4 +1,5 @@
use bevy::{ use bevy::{
math::Vec3,
prelude::{Component, ReflectResource, Resource}, prelude::{Component, ReflectResource, Resource},
reflect::Reflect, reflect::Reflect,
}; };
@ -6,12 +7,18 @@ use bevy::{
#[derive(Component)] #[derive(Component)]
pub struct CyberBikeBody; pub struct CyberBikeBody;
#[derive(Component)] #[derive(Clone, Copy, Component)]
pub struct CyberSteering; pub struct CyberSteering;
#[derive(Debug, Component)] #[derive(Clone, Copy, Debug, Component)]
pub struct CyberWheel; pub struct CyberWheel;
#[derive(Clone, Debug, Component)]
pub struct CyberSpring(pub Vec3);
#[derive(Clone, Debug, Component)]
pub struct CyberFork;
#[derive(Resource, Reflect)] #[derive(Resource, Reflect)]
#[reflect(Resource)] #[reflect(Resource)]
pub struct WheelConfig { pub struct WheelConfig {
@ -35,7 +42,7 @@ impl Default for WheelConfig {
rear_back: 1.0, rear_back: 1.0,
y: -0.1, y: -0.1,
limits: [-0.5, 0.1], limits: [-0.5, 0.1],
stiffness: 190.0, stiffness: 3.0,
damping: 8.0, damping: 8.0,
radius: 0.25, radius: 0.25,
thickness: 0.11, thickness: 0.11,

View file

@ -2,16 +2,16 @@ mod body;
mod components; mod components;
mod wheels; mod wheels;
use bevy::prelude::{ use bevy::{
App, Assets, IntoSystemConfig, Mesh, Plugin, ResMut, StandardMaterial, StartupSet, app::{PostStartup, Startup},
prelude::{App, Assets, Mesh, Plugin, ResMut, StandardMaterial},
}; };
use bevy_rapier3d::prelude::Group;
pub(crate) use self::components::*; pub(crate) use self::components::*;
use self::{body::spawn_cyberbike, wheels::spawn_wheels}; use self::{body::spawn_cyberbike, wheels::spawn_wheels};
pub const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1); pub const BIKE_BODY_COLLISION_GROUP: (u32, u32) = (0b01, 0b01);
pub const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10); pub const BIKE_WHEEL_COLLISION_GROUP: (u32, u32) = (0b10, 0b10);
type Meshterial<'a> = ( type Meshterial<'a> = (
ResMut<'a, Assets<Mesh>>, ResMut<'a, Assets<Mesh>>,
@ -23,6 +23,6 @@ impl Plugin for CyberBikePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(WheelConfig::default()) app.insert_resource(WheelConfig::default())
.register_type::<WheelConfig>() .register_type::<WheelConfig>()
.add_startup_system(spawn_cyberbike.in_base_set(StartupSet::PostStartup)); .add_systems(Startup, spawn_cyberbike);
} }
} }

View file

@ -1,11 +1,10 @@
use bevy::prelude::{shape::Torus as Tire, *}; use avian3d::prelude::*;
use bevy_rapier3d::prelude::{ use bevy::prelude::{Torus as Tire, *};
Ccd, CoefficientCombineRule, Collider, ColliderMassProperties, CollisionGroups, Damping,
ExternalForce, Friction, MultibodyJoint, PrismaticJointBuilder, Restitution,
RevoluteJointBuilder, RigidBody, Sleeping, TransformInterpolation,
};
use super::{CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP}; use super::{
CyberFork, CyberSpring, CyberSteering, CyberWheel, Meshterial, WheelConfig,
BIKE_WHEEL_COLLISION_GROUP,
};
pub fn spawn_wheels( pub fn spawn_wheels(
commands: &mut Commands, commands: &mut Commands,
@ -14,18 +13,18 @@ pub fn spawn_wheels(
meshterials: &mut Meshterial, meshterials: &mut Meshterial,
) { ) {
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP; let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
let wheels_collision_group = CollisionGroups::new(membership, filter); let wheels_collision_group = CollisionLayers::new(membership, filter);
let wheel_y = conf.y; let wheel_y = conf.y;
let stiffness = conf.stiffness; let not_sleeping = SleepingDisabled;
let not_sleeping = Sleeping::disabled(); let ccd = SweptCcd::NON_LINEAR.include_dynamic(false);
let ccd = Ccd { enabled: true };
let limits = conf.limits; let limits = conf.limits;
let (meshes, materials) = meshterials; let (meshes, materials) = meshterials;
let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30 degrees of rake let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30 degrees of rake
let friction = Friction { let friction = Friction {
coefficient: conf.friction, dynamic_coefficient: conf.friction,
combine_rule: CoefficientCombineRule::Average, static_coefficient: conf.friction,
combine_rule: CoefficientCombine::Average,
}; };
let mut wheel_poses = Vec::with_capacity(2); let mut wheel_poses = Vec::with_capacity(2);
@ -50,12 +49,7 @@ pub fn spawn_wheels(
let (mesh, collider) = gen_tires(conf); let (mesh, collider) = gen_tires(conf);
let material = StandardMaterial { let material = StandardMaterial {
base_color: Color::Rgba { base_color: Color::linear_rgba(0.01, 0.01, 0.01, 1.0),
red: 0.01,
green: 0.01,
blue: 0.01,
alpha: 1.0,
},
alpha_mode: AlphaMode::Opaque, alpha_mode: AlphaMode::Opaque,
perceptual_roughness: 0.5, perceptual_roughness: 0.5,
..Default::default() ..Default::default()
@ -67,64 +61,70 @@ pub fn spawn_wheels(
..Default::default() ..Default::default()
}; };
let mass_props = ColliderMassProperties::Density(conf.density);
let suspension_damping = conf.damping;
let suspension_axis = if steering.is_some() { let suspension_axis = if steering.is_some() {
rake_vec rake_vec
} else { } else {
Vec3::Y Vec3::Y
}; };
let suspension_joint_builder = PrismaticJointBuilder::new(suspension_axis) let wheel = commands
.local_anchor1(offset) .spawn(pbr_bundle)
.limits(limits) .insert((
.motor_position(limits[0], stiffness, suspension_damping);
let suspension_joint = MultibodyJoint::new(bike, suspension_joint_builder);
let fork_rb_entity = commands
.spawn(RigidBody::Dynamic)
.insert(suspension_joint)
.insert(not_sleeping)
.id();
let axel_parent_entity = if let Some(steering) = steering {
let neck_builder =
RevoluteJointBuilder::new(rake_vec).local_anchor1(Vec3::new(0.0, 0.0, 0.1)); // this adds another 0.1m of trail
let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder);
let neck = commands
.spawn(RigidBody::Dynamic)
.insert(neck_joint)
.insert(steering)
.insert(not_sleeping)
.id();
neck
} else {
fork_rb_entity
};
let axel_builder = RevoluteJointBuilder::new(Vec3::X);
let axel_joint = MultibodyJoint::new(axel_parent_entity, axel_builder);
let wheel_damping = Damping {
linear_damping: 0.8,
..Default::default()
};
commands.spawn(pbr_bundle).insert((
collider, collider,
mass_props,
wheel_damping,
ccd, ccd,
not_sleeping, not_sleeping,
axel_joint,
wheels_collision_group, wheels_collision_group,
friction, friction,
CyberWheel, CyberWheel,
ExternalForce::default(), ExternalForce::default(),
Restitution::new(conf.restitution), Restitution::new(conf.restitution),
SpatialBundle::default(), SpatialBundle::default(),
TransformInterpolation::default(),
RigidBody::Dynamic, RigidBody::Dynamic,
)); ColliderDensity(0.1),
AngularVelocity::ZERO,
ExternalTorque::ZERO,
))
.id();
let spring = CyberSpring(suspension_axis);
let axel = if let Some(steering) = steering {
commands.spawn((
RigidBody::Dynamic,
ExternalForce::ZERO,
steering,
spring,
Collider::sphere(0.1),
ColliderDensity(0.1),
))
} else {
commands.spawn((
RigidBody::Dynamic,
ExternalForce::ZERO,
spring,
Collider::sphere(0.1),
ColliderDensity(0.1),
))
}
.insert(SpatialBundle::default())
.id();
let axel_joint = RevoluteJoint::new(axel, wheel).with_aligned_axis(Vec3::NEG_X);
commands.spawn(axel_joint);
// suspension and steering:
if steering.is_some() {
let joint = PrismaticJoint::new(axel, bike)
.with_free_axis(suspension_axis)
.with_local_anchor_1(Vec3::new(0.0, 0.0, 0.1))
.with_local_anchor_2(offset)
.with_limits(limits[0], limits[1]);
commands.spawn((joint, CyberFork));
} else {
let joint = PrismaticJoint::new(axel, bike)
.with_free_axis(suspension_axis)
.with_local_anchor_2(offset)
.with_limits(limits[0], limits[1]);
commands.spawn(joint);
};
} }
} }
@ -133,9 +133,8 @@ fn gen_tires(conf: &WheelConfig) -> (Mesh, Collider) {
let wheel_rad = conf.radius; let wheel_rad = conf.radius;
let tire_thickness = conf.thickness; let tire_thickness = conf.thickness;
let tire = Tire { let tire = Tire {
radius: wheel_rad, minor_radius: tire_thickness,
ring_radius: tire_thickness, major_radius: wheel_rad,
..Default::default()
}; };
let mut mesh = Mesh::from(tire); let mut mesh = Mesh::from(tire);
@ -161,17 +160,7 @@ fn gen_tires(conf: &WheelConfig) -> (Mesh, Collider) {
for idx in indices.as_slice().chunks_exact(3) { for idx in indices.as_slice().chunks_exact(3) {
idxs.push([idx[0] as u32, idx[1] as u32, idx[2] as u32]); idxs.push([idx[0] as u32, idx[1] as u32, idx[2] as u32]);
} }
let wheel_collider = Collider::convex_decomposition( let wheel_collider = Collider::convex_decomposition_from_mesh(&mesh).unwrap();
&mesh
.attribute(Mesh::ATTRIBUTE_POSITION)
.unwrap()
.as_float3()
.unwrap()
.iter()
.map(|v| Vec3::from_array(*v))
.collect::<Vec<_>>(),
&idxs,
);
(mesh, wheel_collider) (mesh, wheel_collider)
} }

View file

@ -81,7 +81,7 @@ fn follow_cyberbike(
// handle input pitch // handle input pitch
let angle = input.pitch.powi(3) * MAX_PITCH; let angle = input.pitch.powi(3) * MAX_PITCH;
let axis = cam_xform.right(); let axis = cam_xform.right();
cam_xform.rotate(Quat::from_axis_angle(axis, angle)); cam_xform.rotate(Quat::from_axis_angle(*axis, angle));
} }
CyberCameras::Debug => { CyberCameras::Debug => {
let mut ncx = bike_xform.to_owned(); let mut ncx = bike_xform.to_owned();
@ -101,7 +101,7 @@ fn update_active_camera(
) { ) {
// find the camera with the current state, set it as the ActiveCamera // find the camera with the current state, set it as the ActiveCamera
query.iter_mut().for_each(|(mut cam, cyber)| { query.iter_mut().for_each(|(mut cam, cyber)| {
if cyber.eq(&state.0) { if cyber.eq(&state.get()) {
cam.is_active = true; cam.is_active = true;
} else { } else {
cam.is_active = false; cam.is_active = false;
@ -112,13 +112,13 @@ fn update_active_camera(
fn cycle_cam_state( fn cycle_cam_state(
state: Res<State<CyberCameras>>, state: Res<State<CyberCameras>>,
mut next: ResMut<NextState<CyberCameras>>, mut next: ResMut<NextState<CyberCameras>>,
mut keys: ResMut<Input<KeyCode>>, mut keys: ResMut<ButtonInput<KeyCode>>,
) { ) {
if keys.just_pressed(KeyCode::D) { if keys.just_pressed(KeyCode::KeyD) {
let new_state = state.0.next(); let new_state = state.get().next();
info!("{:?}", new_state); info!("{:?}", new_state);
next.set(new_state); next.set(new_state);
keys.reset(KeyCode::D); keys.reset(KeyCode::KeyD);
} }
} }
@ -132,9 +132,10 @@ impl Plugin for CyberCamPlugin {
fn common(app: &mut bevy::prelude::App) { fn common(app: &mut bevy::prelude::App) {
app.insert_resource(DebugCamOffset::default()) app.insert_resource(DebugCamOffset::default())
.add_startup_system(setup_cybercams) .add_systems(Startup, setup_cybercams)
.add_state::<CyberCameras>() .init_state::<CyberCameras>()
.add_system(cycle_cam_state) .add_systems(
.add_system(update_active_camera) Update,
.add_system(follow_cyberbike); (cycle_cam_state, update_active_camera, follow_cyberbike),
);
} }

View file

@ -1,4 +1,9 @@
use bevy::prelude::{App, Color, Plugin}; use avian3d::debug_render::PhysicsGizmos;
use bevy::{
color::Srgba,
gizmos::AppGizmoBuilder,
prelude::{App, Color, GizmoConfig, Plugin},
};
// use crate::planet::CyberPlanet; // use crate::planet::CyberPlanet;
@ -9,26 +14,16 @@ pub struct CyberGlamorPlugin;
impl Plugin for CyberGlamorPlugin { impl Plugin for CyberGlamorPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
{ {
use bevy_rapier3d::render::{ let plugin = avian3d::debug_render::PhysicsDebugPlugin::default();
DebugRenderMode, DebugRenderStyle, RapierDebugRenderPlugin,
}; app.add_plugins(plugin).insert_gizmo_config(
let style = DebugRenderStyle { PhysicsGizmos {
multibody_joint_anchor_color: Color::GREEN.as_rgba_f32(), contact_point_color: Some(Srgba::GREEN.into()),
contact_normal_color: Some(Srgba::WHITE.into()),
..Default::default() ..Default::default()
}; },
let mode = DebugRenderMode::CONTACTS GizmoConfig::default(),
| DebugRenderMode::SOLVER_CONTACTS );
| DebugRenderMode::JOINTS
| DebugRenderMode::RIGID_BODY_AXES;
let rplugin = RapierDebugRenderPlugin {
style,
always_on_top: true,
enabled: true,
mode,
};
app.add_plugin(rplugin);
} }
} }
} }

View file

@ -14,22 +14,22 @@ pub(crate) struct InputState {
pub pitch: f32, pub pitch: f32,
} }
fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<Input<KeyCode>>) { fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<ButtonInput<KeyCode>>) {
let keyset: HashSet<_> = keys.get_pressed().collect(); let keyset: HashSet<_> = keys.get_pressed().collect();
let shifted = keyset.contains(&KeyCode::LShift) || keyset.contains(&KeyCode::RShift); let shifted = keyset.contains(&KeyCode::ShiftLeft) || keyset.contains(&KeyCode::ShiftRight);
for key in keyset { for key in keyset {
match key { match key {
KeyCode::Left => offset.rot -= 5.0, KeyCode::ArrowLeft => offset.rot -= 5.0,
KeyCode::Right => offset.rot += 5.0, KeyCode::ArrowRight => offset.rot += 5.0,
KeyCode::Up => { KeyCode::ArrowUp => {
if shifted { if shifted {
offset.alt += 0.5; offset.alt += 0.5;
} else { } else {
offset.dist -= 0.5; offset.dist -= 0.5;
} }
} }
KeyCode::Down => { KeyCode::ArrowDown => {
if shifted { if shifted {
offset.alt -= 0.5; offset.alt -= 0.5;
} else { } else {
@ -41,16 +41,17 @@ fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<Input<K
} }
if keys.get_just_released().len() > 0 { if keys.get_just_released().len() > 0 {
let unpressed = keys.just_released(KeyCode::LShift) || keys.just_released(KeyCode::RShift); let unpressed =
keys.just_released(KeyCode::ShiftLeft) || keys.just_released(KeyCode::ShiftRight);
keys.reset_all(); keys.reset_all();
if shifted && !unpressed { if shifted && !unpressed {
keys.press(KeyCode::LShift); keys.press(KeyCode::ShiftLeft);
} }
} }
} }
fn update_input(mut events: EventReader<GamepadEvent>, mut istate: ResMut<InputState>) { fn update_input(mut events: EventReader<GamepadEvent>, mut istate: ResMut<InputState>) {
for pad_event in events.iter() { for pad_event in events.read() {
match pad_event { match pad_event {
GamepadEvent::Button(button_event) => { GamepadEvent::Button(button_event) => {
let GamepadButtonChangedEvent { let GamepadButtonChangedEvent {
@ -92,7 +93,6 @@ pub struct CyberInputPlugin;
impl Plugin for CyberInputPlugin { impl Plugin for CyberInputPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<InputState>() app.init_resource::<InputState>()
.add_system(update_input) .add_systems(Update, (update_input, update_debug_cam));
.add_system(update_debug_cam);
} }
} }

View file

@ -4,6 +4,9 @@ use crate::planet::PLANET_RADIUS;
pub const LIGHT_RANGE: f32 = 90.0; pub const LIGHT_RANGE: f32 = 90.0;
static BLUE: Color = Color::linear_rgb(0.0, 0.0, 1.0);
static PINK: Color = Color::linear_rgb(199.0 / 255.0, 21.0 / 255.0, 113.0 / 255.0);
fn spawn_static_lights( fn spawn_static_lights(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
@ -12,7 +15,7 @@ fn spawn_static_lights(
let pink_light = PointLight { let pink_light = PointLight {
intensity: 1_00.0, intensity: 1_00.0,
range: LIGHT_RANGE, range: LIGHT_RANGE,
color: Color::PINK, color: PINK,
radius: 1.0, radius: 1.0,
shadows_enabled: true, shadows_enabled: true,
..Default::default() ..Default::default()
@ -21,7 +24,7 @@ fn spawn_static_lights(
let blue_light = PointLight { let blue_light = PointLight {
intensity: 1_000.0, intensity: 1_000.0,
range: LIGHT_RANGE, range: LIGHT_RANGE,
color: Color::BLUE, color: BLUE,
radius: 1.0, radius: 1.0,
shadows_enabled: true, shadows_enabled: true,
..Default::default() ..Default::default()
@ -48,16 +51,10 @@ fn spawn_static_lights(
}) })
.with_children(|builder| { .with_children(|builder| {
builder.spawn(PbrBundle { builder.spawn(PbrBundle {
mesh: meshes.add( mesh: meshes.add(Mesh::from(Sphere::new(10.0))),
Mesh::try_from(shape::Icosphere {
radius: 10.0,
subdivisions: 2,
})
.unwrap(),
),
material: materials.add(StandardMaterial { material: materials.add(StandardMaterial {
base_color: Color::BLUE, base_color: BLUE,
emissive: Color::PINK, emissive: PINK.into(),
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
@ -72,16 +69,10 @@ fn spawn_static_lights(
}) })
.with_children(|builder| { .with_children(|builder| {
builder.spawn(PbrBundle { builder.spawn(PbrBundle {
mesh: meshes.add( mesh: meshes.add(Mesh::from(Sphere::new(10.0))),
Mesh::try_from(shape::Icosphere {
radius: 10.0,
subdivisions: 2,
})
.unwrap(),
),
material: materials.add(StandardMaterial { material: materials.add(StandardMaterial {
base_color: Color::PINK, base_color: PINK,
emissive: Color::BLUE, emissive: BLUE.into(),
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
@ -92,6 +83,6 @@ fn spawn_static_lights(
pub struct CyberSpaceLightsPlugin; pub struct CyberSpaceLightsPlugin;
impl Plugin for CyberSpaceLightsPlugin { impl Plugin for CyberSpaceLightsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_startup_system(spawn_static_lights); app.add_systems(Startup, spawn_static_lights);
} }
} }

View file

@ -8,7 +8,7 @@ use cyber_rider::{
}; };
//const CYBER_SKY: Color = Color::rgb(0.07, 0.001, 0.02); //const CYBER_SKY: Color = Color::rgb(0.07, 0.001, 0.02);
const CYBER_SKY: Color = Color::rgb(0.64, 0.745, 0.937); // a light blue sky const CYBER_SKY: Color = Color::srgb(0.64, 0.745, 0.937); // a light blue sky
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
@ -21,18 +21,36 @@ fn main() {
}), }),
..Default::default() ..Default::default()
})) }))
.add_plugin(CyberPlanetPlugin) .add_plugins((
.add_plugin(CyberInputPlugin) CyberPlanetPlugin,
.add_plugin(CyberActionPlugin) CyberInputPlugin,
.add_plugin(CyberCamPlugin) CyberActionPlugin,
.add_plugin(CyberSpaceLightsPlugin) CyberCamPlugin,
.add_plugin(CyberUIPlugin) CyberSpaceLightsPlugin,
.add_plugin(CyberBikePlugin) CyberUIPlugin,
.add_startup_system(disable_mouse_trap) CyberBikePlugin,
.add_system(bevy::window::close_on_esc); ))
.add_systems(Startup, disable_mouse_trap)
.add_systems(Update, close_on_esc);
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
app.add_plugin(CyberGlamorPlugin); app.add_plugin(CyberGlamorPlugin);
app.run(); app.run();
} }
fn close_on_esc(
mut commands: Commands,
focused_windows: Query<(Entity, &Window)>,
input: Res<ButtonInput<KeyCode>>,
) {
for (window, focus) in focused_windows.iter() {
if !focus.focused {
continue;
}
if input.just_pressed(KeyCode::Escape) {
commands.entity(window).despawn();
}
}
}

View file

@ -1,8 +1,8 @@
use avian3d::prelude::*;
use bevy::{ use bevy::{
prelude::{shape::Icosphere, *}, prelude::*,
render::{color::Color, mesh::Indices}, render::{mesh::Indices, render_asset::RenderAssetUsages},
}; };
use bevy_rapier3d::prelude::*;
use hexasphere::shapes::IcoSphere; use hexasphere::shapes::IcoSphere;
use noise::{HybridMulti, NoiseFn, SuperSimplex}; use noise::{HybridMulti, NoiseFn, SuperSimplex};
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
@ -20,20 +20,12 @@ fn spawn_planet(
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
//let color = Color::rgb(0.74, 0.5334, 0.176); let (mesh, shape) = gen_planet(PLANET_RADIUS);
let isphere = Icosphere {
radius: PLANET_RADIUS,
subdivisions: 88,
};
let (mesh, shape) = gen_planet(isphere);
let pbody = (RigidBody::Fixed, Ccd { enabled: true });
let pcollide = ( let pcollide = (
shape, shape,
Friction { Friction {
coefficient: 1.2, static_coefficient: 1.2,
..Default::default() ..Default::default()
}, },
Restitution::new(0.8), Restitution::new(0.8),
@ -42,18 +34,22 @@ fn spawn_planet(
commands commands
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add(mesh), mesh: meshes.add(mesh),
material: materials.add(Color::WHITE.into()), material: materials.add(Color::WHITE),
..Default::default() ..Default::default()
}) })
.insert(pbody) .insert((
.insert(pcollide) RigidBody::Static,
.insert(CyberPlanet); pcollide,
CyberPlanet,
CollisionLayers::new(u32::MAX, u32::MAX),
CollisionMargin(0.2),
));
} }
pub struct CyberPlanetPlugin; pub struct CyberPlanetPlugin;
impl Plugin for CyberPlanetPlugin { impl Plugin for CyberPlanetPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_startup_system(spawn_planet); app.add_systems(Startup, spawn_planet);
} }
} }
@ -61,10 +57,10 @@ impl Plugin for CyberPlanetPlugin {
// utils // utils
//--------------------------------------------------------------------- //---------------------------------------------------------------------
fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) { fn gen_planet(radius: f32) -> (Mesh, Collider) {
// straight-up stolen from Bevy's impl of Mesh from Icosphere, so I can do the // straight-up stolen from Bevy's impl of Mesh from Icosphere, so I can do the
// displacement before normals are calculated. // displacement before normals are calculated.
let generated = IcoSphere::new(sphere.subdivisions, |point| { let generated = IcoSphere::new(79, |point| {
let inclination = point.y.acos(); let inclination = point.y.acos();
let azimuth = point.z.atan2(point.x); let azimuth = point.z.atan2(point.x);
@ -90,7 +86,7 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
let points = noisy_points let points = noisy_points
.iter() .iter()
.map(|&p| (Vec3::from_slice(&p) * sphere.radius).into()) .map(|&p| (Vec3::from_slice(&p) * radius).into())
.collect::<Vec<[f32; 3]>>(); .collect::<Vec<[f32; 3]>>();
for p in &points { for p in &points {
@ -108,10 +104,13 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
} }
let indices = Indices::U32(indices); let indices = Indices::U32(indices);
let collider = Collider::trimesh(points.iter().map(|p| Vect::from_slice(p)).collect(), idxs); let collider = Collider::trimesh(points.iter().map(|p| Vec3::from_slice(p)).collect(), idxs);
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); let mut mesh = Mesh::new(
mesh.set_indices(Some(indices)); PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
);
mesh.insert_indices(indices);
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points);
//mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); //mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
mesh.duplicate_vertices(); mesh.duplicate_vertices();
@ -129,7 +128,9 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
let l = 0.41; let l = 0.41;
let jitter = rng.gen_range(-0.0..=360.0f32); let jitter = rng.gen_range(-0.0..=360.0f32);
let h = jitter; let h = jitter;
let color = Color::hsl(h, PLANET_SATURATION, l).as_linear_rgba_f32(); let color = Color::hsl(h, PLANET_SATURATION, l)
.to_linear()
.to_f32_array();
for _ in 0..3 { for _ in 0..3 {
colors.push(color); colors.push(color);
} }

View file

@ -1,10 +1,13 @@
use bevy::prelude::{ use avian3d::prelude::LinearVelocity;
use bevy::{
app::{Startup, Update},
prelude::{
AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text, AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text,
TextBundle, TextSection, TextStyle, Transform, With, TextBundle, TextSection, TextStyle, Transform, With,
},
}; };
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
use bevy_inspector_egui::quick::WorldInspectorPlugin; use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_rapier3d::prelude::Velocity;
use crate::bike::CyberBikeBody; use crate::bike::CyberBikeBody;
@ -26,7 +29,7 @@ fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
style: TextStyle { style: TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"), font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 40.0, font_size: 40.0,
color: Color::GOLD, color: Color::srgba_u8(255, 215, 0, 230),
}, },
}], }],
..Default::default() ..Default::default()
@ -37,12 +40,12 @@ fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
} }
fn update_ui( fn update_ui(
state_query: Query<(&Velocity, &Transform), With<CyberBikeBody>>, state_query: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
mut text_query: Query<&mut Text, With<UpText>>, mut text_query: Query<&mut Text, With<UpText>>,
) { ) {
let mut text = text_query.single_mut(); let mut text = text_query.single_mut();
let (velocity, xform) = state_query.single(); let (velocity, xform) = state_query.single();
let speed = velocity.linvel.dot(xform.forward()); let speed = velocity.dot(*xform.forward());
text.sections[0].value = format!("spd: {:.2}", speed); text.sections[0].value = format!("spd: {:.2}", speed);
} }
@ -53,6 +56,7 @@ impl Plugin for CyberUIPlugin {
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
app.add_plugin(WorldInspectorPlugin); app.add_plugin(WorldInspectorPlugin);
app.add_startup_system(setup_ui).add_system(update_ui); app.add_systems(Startup, setup_ui)
.add_systems(Update, update_ui);
} }
} }