use std::fmt::Debug; use bevy::prelude::{shape::Capsule 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>, 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: 1.2, rear_back: 1.1, y: -1.0, limits: [0.1, 1.0], stiffness: 10.0, damping: 0.7, 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, wheel_conf: Res, 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(); //return; re_tire(&mut commands, &xform, bike, &wheel_conf, &mut meshterials); } fn re_tire( 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, rings: 1, depth: 0.2, ..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 = -conf.front_stance; let wheel_z = conf.front_forward; 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 = 0.3; let prismatic = PrismaticJointBuilder::new(Vec3::Y) .local_anchor2(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::() .add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike); } }