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; type Meshterial<'a> = ( ResMut<'a, Assets>, ResMut<'a, Assets>, ); #[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, 0.0], stiffness: 80.0, damping: 0.6, radius: 0.4, } } } 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, wheel_conf: Res, mut meshterials: Meshterial, ) { let altitude = PLANET_RADIUS - 220.0; let mut xform = Transform::from_translation(Vec3::X * altitude) .with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians())); //.with_rotation(Quat::from_axis_angle(Vec3::X, 140.0f32.to_radians())); let right = xform.right() * 350.0; xform.translation += right; 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, mut meshterials: Meshterial, bquery: Query<(Entity, &Transform), With>, wheels: Query>, ) { // 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(-1.0, stiffness, damping); let joint = ImpulseJoint::new(bike, prismatic); let spatial_bundle = SpatialBundle { transform: wheel_pos_in_world, ..Default::default() }; let tire_spundle = SpatialBundle { transform: Transform::IDENTITY, ..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( TransformInterpolation { start: None, end: None, }, ); }) .insert(TransformInterpolation { start: None, end: None, }) .insert(CyberWheel); } } pub struct CyberBikePlugin; impl Plugin for CyberBikePlugin { fn build(&self, app: &mut App) { app.insert_resource(WheelConfig::default()) .register_type::() .add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike) .add_system(re_tire); } }