From 07a0c1f05946e32f8b3ef83d175c0199091ea961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lescaudey=20de=20Maneville?= Date: Mon, 5 Feb 2024 10:33:30 +0100 Subject: [PATCH 1/5] FPS Character example --- .../examples/character_controller3.rs | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 bevy_rapier3d/examples/character_controller3.rs diff --git a/bevy_rapier3d/examples/character_controller3.rs b/bevy_rapier3d/examples/character_controller3.rs new file mode 100644 index 00000000..530bce9e --- /dev/null +++ b/bevy_rapier3d/examples/character_controller3.rs @@ -0,0 +1,216 @@ +use bevy::{ + input::{mouse::MouseMotion, InputSystem}, + prelude::*, +}; +use bevy_rapier3d::{control::KinematicCharacterController, prelude::*}; + +const MOUSE_SENSITIVITY: f32 = 0.3; +const GROUND_TIMER: f32 = 0.5; +const MOVEMENT_SPEED: f32 = 8.0; +const JUMP_SPEED: f32 = 20.0; +const GRAVITY: f32 = -9.81; + +fn main() { + App::new() + // .insert_resource(ClearColor(Color::rgb( + // 0xF9 as f32 / 255.0, + // 0xF9 as f32 / 255.0, + // 0xFF as f32 / 255.0, + // ))) + .init_resource::() + .init_resource::() + .add_plugins(( + DefaultPlugins, + RapierPhysicsPlugin::::default(), + RapierDebugRenderPlugin::default(), + )) + .add_systems(Startup, (setup_player, setup_map)) + .add_systems(PreUpdate, handle_input.after(InputSystem)) + .add_systems(Update, player_look) + .add_systems(FixedUpdate, player_movement) + .run(); +} + +pub fn setup_player(mut commands: Commands) { + commands + .spawn(( + SpatialBundle { + transform: Transform::from_xyz(0.0, 5.0, 0.0), + ..default() + }, + Collider::round_cylinder(0.9, 0.3, 0.2), + KinematicCharacterController { + custom_mass: Some(5.0), + up: Vec3::Y, + offset: CharacterLength::Absolute(0.01), + slide: true, + autostep: Some(CharacterAutostep { + max_height: CharacterLength::Relative(0.3), + min_width: CharacterLength::Relative(0.5), + include_dynamic_bodies: false, + }), + // Don’t allow climbing slopes larger than 45 degrees. + max_slope_climb_angle: 45.0_f32.to_radians(), + // Automatically slide down on slopes smaller than 30 degrees. + min_slope_slide_angle: 30.0_f32.to_radians(), + apply_impulse_to_dynamic_bodies: true, + snap_to_ground: None, + ..default() + }, + )) + .with_children(|b| { + b.spawn(Camera3dBundle { + transform: Transform::from_xyz(0.0, 0.2, -0.1), + ..Default::default() + }); + }); +} + +fn setup_map(mut commands: Commands) { + /* + * Ground + */ + let ground_size = 50.0; + let ground_height = 0.1; + + commands.spawn(( + TransformBundle::from(Transform::from_xyz(0.0, -ground_height, 0.0)), + Collider::cuboid(ground_size, ground_height, ground_size), + )); + /* + * Stairs + */ + let stair_len = 30; + let stair_step = 0.1; + for i in 1..=stair_len { + let step = i as f32; + let collider = Collider::cuboid(1.0, step * stair_step, 1.0); + commands.spawn(( + TransformBundle::from(Transform::from_xyz( + 40.0, + step * stair_step, + step * 2.0 - 20.0, + )), + collider.clone(), + )); + commands.spawn(( + TransformBundle::from(Transform::from_xyz( + -40.0, + step * stair_step, + step * -2.0 + 20.0, + )), + collider.clone(), + )); + commands.spawn(( + TransformBundle::from(Transform::from_xyz( + step * 2.0 - 20.0, + step * stair_step, + 40.0, + )), + collider.clone(), + )); + commands.spawn(( + TransformBundle::from(Transform::from_xyz( + step * -2.0 + 20.0, + step * stair_step, + -40.0, + )), + collider.clone(), + )); + } +} + +#[derive(Default, Resource, Deref, DerefMut)] +struct MovementInput(Vec3); + +#[derive(Default, Resource, Deref, DerefMut)] +struct LookInput(Vec2); + +fn handle_input( + keyboard: Res>, + mut movement: ResMut, + mut look: ResMut, + mut mouse_events: EventReader, +) { + if keyboard.pressed(KeyCode::W) { + movement.z -= 1.0; + } + if keyboard.pressed(KeyCode::S) { + movement.z += 1.0 + } + if keyboard.pressed(KeyCode::A) { + movement.x -= 1.0; + } + if keyboard.pressed(KeyCode::D) { + movement.x += 1.0 + } + **movement = movement.normalize_or_zero(); + if keyboard.pressed(KeyCode::ShiftLeft) { + **movement *= 2.0; + } + if keyboard.pressed(KeyCode::Space) { + movement.y = 1.0; + } + + for event in mouse_events.read() { + look.x -= event.delta.x * MOUSE_SENSITIVITY; + look.y -= event.delta.y * MOUSE_SENSITIVITY; + look.y = look.y.clamp(-89.9, 89.9); // Limit pitch + } +} + +fn player_movement( + time: Res