use bevy::prelude::*; use bevy_rapier3d::prelude::*; use crate::planet::PLANET_RADIUS; pub(crate) const SPAWN_ALTITUDE: f32 = PLANET_RADIUS * 1.01; #[derive(Component)] pub struct CyberBikeBody; #[derive(Component)] pub struct CyberBikeCollider; #[derive(Component, Debug)] pub struct CyberBikeModel; #[derive(Component, Debug, Default, Clone, Copy)] pub struct CyberBikeControl { pub roll_sum: f32, pub prev_roll_error: f32, pub pitch_sum: f32, pub prev_pitch_error: f32, } const BIKE_BODY_GROUP: InteractionGroups = InteractionGroups::new(0b01, 0b01); const BIKE_WHEEL_GROUP: InteractionGroups = InteractionGroups::new(0b10, 0b10); fn spawn_cyberbike(mut commands: Commands, asset_server: Res) { let xform = Transform::from_translation(Vec3::X * SPAWN_ALTITUDE) .with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians())); let mut bbody = RigidBodyBundle::default(); bbody.damping.angular_damping = 0.5; bbody.damping.linear_damping = 0.1; bbody.activation = RigidBodyActivation::cannot_sleep().into(); bbody.ccd = RigidBodyCcd { ccd_enabled: true, ccd_thickness: 0.2, ccd_max_dist: 2.7, ..Default::default() } .into(); let isometry = Isometry::from_parts(xform.translation.into(), xform.rotation.into()); bbody.position = isometry.into(); let shape = ColliderShape::capsule( Vec3::new(0.0, 0.0, -2.7).into(), Vec3::new(0.0, 0.0, 2.5).into(), 1.0, ); let bcollide = ColliderBundle { shape: shape.into(), mass_properties: ColliderMassProps::Density(0.2).into(), flags: ColliderFlags { collision_groups: BIKE_BODY_GROUP, ..Default::default() } .into(), material: ColliderMaterial { friction: 0.0, restitution: 0.0, ..Default::default() } .into(), ..Default::default() }; let bike = commands .spawn_bundle(bbody) .insert_bundle((xform, GlobalTransform::default())) .insert(RigidBodyPositionSync::Interpolated { prev_pos: None }) .with_children(|child_builder| { child_builder .spawn_bundle(bcollide) .insert(ColliderDebugRender { color: Color::GREEN, }) .insert(CyberBikeCollider) .insert(ColliderPositionSync::Discrete); }) .with_children(|rider| { rider.spawn_scene(asset_server.load("cyber-bike_no_y_up.glb#Scene0")); }) .insert(CyberBikeModel) .insert(CyberBikeBody) .insert(CyberBikeControl::default()) .id(); // seriously the cyberbike is so fucking huge what the heck let wheel_positions = vec![-5.1, 4.7, -5.1]; let wheel_y = -1.8f32; for (i, &wheel_z) in wheel_positions.iter().enumerate() { let (wheel_x, wheel_rad, stiffness) = match i { 0 => (-2.6, 1.0, 0.016), 2 => (2.6, 1.0, 0.016), 1 => (0.0, 1.2, 0.020), _ => unreachable!(), }; let offset = Vec3::new(wheel_x, wheel_y, wheel_z); let trans = xform.translation + offset; let wheel_pos_in_world = Isometry::from_parts(trans.into(), xform.rotation.into()); let _wheel_rb = commands .spawn_bundle(RigidBodyBundle { position: wheel_pos_in_world.into(), activation: RigidBodyActivation::cannot_sleep().into(), damping: RigidBodyDamping { angular_damping: 0.8, ..Default::default() } .into(), ccd: RigidBodyCcd { ccd_enabled: true, ccd_max_dist: wheel_rad, ccd_thickness: 0.01, ..Default::default() } .into(), ..Default::default() }) .insert_bundle(ColliderBundle { material: ColliderMaterial::new(0.0, 0.0).into(), shape: ColliderShape::ball(wheel_rad).into(), mass_properties: ColliderMassProps::Density(0.001).into(), flags: ColliderFlags { collision_groups: BIKE_WHEEL_GROUP, ..Default::default() } .into(), ..Default::default() }) .insert(ColliderPositionSync::Discrete) .insert(ColliderDebugRender::from(Color::YELLOW)) .id(); let damping = 0.3; let prismatic = PrismaticJoint::new(Vector::y_axis()) .local_anchor1(offset.into()) .motor_position(-0.4, stiffness, damping); commands.spawn_bundle(((JointBuilderComponent::new(prismatic, bike, _wheel_rb)),)); } } pub struct CyberBikePlugin; impl Plugin for CyberBikePlugin { #[cfg(feature = "debug_render")] fn build(&self, app: &mut App) { app.add_plugin(RapierRenderPlugin) .add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike); } #[cfg(not(feature = "debug_render"))] fn build(&self, app: &mut App) { app.add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike); } }