From d853fdc20d7abf82b33f12969b1fd3ebbb447afb Mon Sep 17 00:00:00 2001 From: qhdwight Date: Fri, 16 Jan 2026 21:05:29 -0800 Subject: [PATCH 1/4] Update to Bevy 0.18 and Avain 0.5 --- Cargo.toml | 6 +++--- examples/minimal_avian.rs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ccb331e..f8d7caf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_fps_controller" -version = "17.1.0" +version = "18.0.0" edition = "2021" authors = ["bevy_fps_controller"] repository = "https://github.com/qhdwight/bevy_fps_controller" @@ -10,9 +10,9 @@ readme = "README.md" description = "Bevy plugin that adds a Source engine inspired FPS movement controller" [dependencies] -bevy = "0.17" +bevy = "0.18" bevy_rapier3d = { version = "0.32", optional = true } -avian3d = { version = "0.4", optional = true } +avian3d = { version = "0.5", optional = true } [features] default = [] diff --git a/examples/minimal_avian.rs b/examples/minimal_avian.rs index 5c5e902..7340081 100644 --- a/examples/minimal_avian.rs +++ b/examples/minimal_avian.rs @@ -15,10 +15,9 @@ const SPAWN_POINT: Vec3 = Vec3::new(0.0, 1.625, 0.0); fn main() { App::new() - .insert_resource(AmbientLight { - color: Color::WHITE, + .insert_resource(GlobalAmbientLight { brightness: 10000.0, - affects_lightmapped_meshes: true, + ..default() }) .insert_resource(ClearColor(Color::linear_rgb(0.83, 0.96, 0.96))) .add_plugins(DefaultPlugins) From 4a355fb7d62c2c6a909a9e5f4d58ecd2627cda31 Mon Sep 17 00:00:00 2001 From: qhdwight Date: Wed, 13 May 2026 20:31:21 -0700 Subject: [PATCH 2/4] Update and fix rapier example --- Cargo.toml | 4 ++-- examples/minimal_rapier.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f8d7caf..3845470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,8 @@ description = "Bevy plugin that adds a Source engine inspired FPS movement contr [dependencies] bevy = "0.18" -bevy_rapier3d = { version = "0.32", optional = true } -avian3d = { version = "0.5", optional = true } +bevy_rapier3d = { version = "0.33", optional = true } +avian3d = { version = "0.6", optional = true } [features] default = [] diff --git a/examples/minimal_rapier.rs b/examples/minimal_rapier.rs index 0864808..09c1692 100644 --- a/examples/minimal_rapier.rs +++ b/examples/minimal_rapier.rs @@ -15,10 +15,10 @@ const SPAWN_POINT: Vec3 = Vec3::new(0.0, 1.625, 0.0); fn main() { App::new() - .insert_resource(AmbientLight { - color: Color::WHITE, + .insert_resource(GlobalAmbientLight { brightness: 10000.0, affects_lightmapped_meshes: true, + ..default() }) .insert_resource(ClearColor(Color::linear_rgb(0.83, 0.96, 0.96))) .add_plugins(DefaultPlugins) From 3ef996edb647d73e71bc8438b3844cc644e58ccc Mon Sep 17 00:00:00 2001 From: qhdwight Date: Wed, 13 May 2026 21:11:38 -0700 Subject: [PATCH 3/4] fix panic --- examples/minimal_rapier.rs | 1 - src/controller_avian.rs | 50 +++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/examples/minimal_rapier.rs b/examples/minimal_rapier.rs index 09c1692..fc9506b 100644 --- a/examples/minimal_rapier.rs +++ b/examples/minimal_rapier.rs @@ -17,7 +17,6 @@ fn main() { App::new() .insert_resource(GlobalAmbientLight { brightness: 10000.0, - affects_lightmapped_meshes: true, ..default() }) .insert_resource(ClearColor(Color::linear_rgb(0.83, 0.96, 0.96))) diff --git a/src/controller_avian.rs b/src/controller_avian.rs index 8d3463c..a5d02d3 100644 --- a/src/controller_avian.rs +++ b/src/controller_avian.rs @@ -1,9 +1,6 @@ use std::f32::consts::*; -use avian3d::{ - parry::{math::Point, shape::SharedShape}, - prelude::*, -}; +use avian3d::{parry::shape::SharedShape, prelude::*}; use bevy::{input::mouse::MouseMotion, math::Vec3Swizzles, prelude::*}; pub struct FpsControllerPlugin; @@ -14,7 +11,12 @@ impl Plugin for FpsControllerPlugin { .add_systems(PreUpdate, clear_fixed_timestep_flag) .add_systems( FixedPreUpdate, - (set_fixed_time_step_flag, fps_controller_move), + ( + set_fixed_time_step_flag, + fps_controller_move, + fps_controller_update_collider, + ) + .chain(), ) .add_systems( RunFixedMainLoop, @@ -251,13 +253,13 @@ pub fn fps_controller_look(mut query: Query<(&mut FpsController, &FpsControllerI pub fn fps_controller_move( time: Res>, - spatial_query_pipeline: Res, + spatial_query_pipeline: SpatialQuery, mut query: Query< ( Entity, &FpsControllerInput, &mut FpsController, - &mut Collider, + &Collider, &mut Transform, &mut LinearVelocity, ), @@ -266,9 +268,7 @@ pub fn fps_controller_move( ) { let dt = time.delta_secs(); - for (entity, input, mut controller, mut collider, mut transform, mut velocity) in - query.iter_mut() - { + for (entity, input, mut controller, collider, mut transform, mut velocity) in query.iter_mut() { controller.previous_translation = Some(transform.translation); if input.fly { @@ -410,17 +410,6 @@ pub fn fps_controller_move( controller.height += dt * crouch_speed; controller.height = controller.height.clamp(crouch_height, upright_height); - if let Some(capsule) = collider.shape().as_capsule() { - let radius = capsule.radius; - let half = Point::from(Vec3::Y * (controller.height * 0.5 - radius)); - collider.set_shape(SharedShape::capsule(-half, half, radius)); - } else if let Some(cylinder) = collider.shape().as_cylinder() { - let radius = cylinder.radius; - collider.set_shape(SharedShape::cylinder(controller.height * 0.5, radius)); - } else { - panic!("Controller must use a cylinder or capsule collider") - } - // Step offset really only works best for cylinders // For capsules the player has to practically teleported to fully step up if collider.shape().as_cylinder().is_some() @@ -520,7 +509,7 @@ fn overhang_component( entity: Entity, collider: &Collider, transform: &Transform, - spatial_query: &SpatialQueryPipeline, + spatial_query: &SpatialQuery, velocity: Vec3, dt: f32, ) -> Option { @@ -590,6 +579,23 @@ fn get_axis(key_input: &Res>, key_pos: KeyCode, key_neg: Ke get_pressed(key_input, key_pos) - get_pressed(key_input, key_neg) } +pub fn fps_controller_update_collider( + mut query: Query<(&FpsController, &mut Collider), With>, +) { + for (controller, mut collider) in query.iter_mut() { + if let Some(capsule) = collider.shape().as_capsule() { + let radius = capsule.radius; + let half = Vec3::Y * (controller.height * 0.5 - radius); + collider.set_shape(SharedShape::capsule(-half, half, radius)); + } else if let Some(cylinder) = collider.shape().as_cylinder() { + let radius = cylinder.radius; + collider.set_shape(SharedShape::cylinder(controller.height * 0.5, radius)); + } else { + panic!("Controller must use a cylinder or capsule collider") + } + } +} + // ____ __ // / __ \___ ____ ____/ /__ _____ // / /_/ / _ \/ __ \/ __ / _ \/ ___/ From 8654cc21c6b392d74e9a61561a223c1cfb9bced1 Mon Sep 17 00:00:00 2001 From: qhdwight Date: Wed, 19 Aug 2026 22:30:31 -0700 Subject: [PATCH 4/4] Update --- Cargo.toml | 8 +++--- examples/minimal_avian.rs | 13 ++++++---- examples/minimal_rapier.rs | 18 +++++++------- src/controller_rapier.rs | 50 +++++++++++++++++++------------------- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3845470..d9d009e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bevy_fps_controller" -version = "18.0.0" +version = "19.0.0" edition = "2021" authors = ["bevy_fps_controller"] repository = "https://github.com/qhdwight/bevy_fps_controller" @@ -10,9 +10,9 @@ readme = "README.md" description = "Bevy plugin that adds a Source engine inspired FPS movement controller" [dependencies] -bevy = "0.18" -bevy_rapier3d = { version = "0.33", optional = true } -avian3d = { version = "0.6", optional = true } +bevy = "0.19" +bevy_rapier3d = { version = "0.36", optional = true } +avian3d = { version = "0.7", optional = true } [features] default = [] diff --git a/examples/minimal_avian.rs b/examples/minimal_avian.rs index 7340081..5bad379 100644 --- a/examples/minimal_avian.rs +++ b/examples/minimal_avian.rs @@ -38,7 +38,7 @@ fn setup(mut commands: Commands, mut window: Query<&mut Window>, assets: Res, assets: Res, assets: Res, assets: Res) { continue; } - velocity.linvel = Vec3::ZERO; + velocity.linear = Vec3::ZERO; transform.translation = SPAWN_POINT; } } @@ -155,7 +155,7 @@ fn scene_colliders( if let Some(gltf) = gltf { let scene = gltf.scenes.first().unwrap().clone(); - commands.spawn(SceneRoot(scene)); + commands.spawn(WorldAssetRoot(scene)); for node in &gltf.nodes { let node = gltf_node_assets.get(node).unwrap(); if let Some(gltf_mesh) = node.mesh.clone() { @@ -208,13 +208,13 @@ fn display_text( for mut text in &mut text_query { text.0 = format!( "vel: {:.2}, {:.2}, {:.2}\npos: {:.2}, {:.2}, {:.2}\nspd: {:.2}", - velocity.linvel.x, - velocity.linvel.y, - velocity.linvel.z, + velocity.linear.x, + velocity.linear.y, + velocity.linear.z, transform.translation.x, transform.translation.y, transform.translation.z, - velocity.linvel.xz().length() + velocity.linear.xz().length() ); } } diff --git a/src/controller_rapier.rs b/src/controller_rapier.rs index ed89600..198a6a6 100644 --- a/src/controller_rapier.rs +++ b/src/controller_rapier.rs @@ -277,9 +277,9 @@ pub fn fps_controller_move( MoveMode::Noclip => { if input.movement == Vec3::ZERO { let friction = controller.fly_friction.clamp(0.0, 1.0); - velocity.linvel *= 1.0 - friction; - if velocity.linvel.length_squared() < f32::EPSILON { - velocity.linvel = Vec3::ZERO; + velocity.linear *= 1.0 - friction; + if velocity.linear.length_squared() < f32::EPSILON { + velocity.linear = Vec3::ZERO; } } else { let fly_speed = if input.sprint { @@ -291,7 +291,7 @@ pub fn fps_controller_move( Mat3::from_euler(EulerRot::YXZ, input.yaw, input.pitch, 0.0); move_to_world.z_axis *= -1.0; // Forward is -Z move_to_world.y_axis = Vec3::Y; // Vertical movement aligned with world up - velocity.linvel = move_to_world * input.movement * fly_speed; + velocity.linear = move_to_world * input.movement * fly_speed; } } MoveMode::Ground => { @@ -336,18 +336,18 @@ pub fn fps_controller_move( // Only apply friction after at least one tick, allows b-hopping without losing speed if controller.ground_tick >= 1 && has_traction { - let lateral_speed = velocity.linvel.xz().length(); + let lateral_speed = velocity.linear.xz().length(); if lateral_speed > controller.friction_speed_cutoff { let control = f32::max(lateral_speed, controller.stop_speed); let drop = control * controller.friction * dt; let new_speed = f32::max((lateral_speed - drop) / lateral_speed, 0.0); - velocity.linvel.x *= new_speed; - velocity.linvel.z *= new_speed; + velocity.linear.x *= new_speed; + velocity.linear.z *= new_speed; } else { - velocity.linvel = Vec3::ZERO; + velocity.linear = Vec3::ZERO; } if controller.ground_tick == 1 { - velocity.linvel.y = -hit.time_of_impact; + velocity.linear.y = -hit.time_of_impact; } } @@ -355,21 +355,21 @@ pub fn fps_controller_move( wish_direction, wish_speed, controller.acceleration, - velocity.linvel, + velocity.linear, dt, ); if !has_traction { add.y -= controller.gravity * dt; } - velocity.linvel += add; + velocity.linear += add; if has_traction { - let linear_velocity = velocity.linvel; - velocity.linvel -= + let linear_velocity = velocity.linear; + velocity.linear -= Vec3::dot(linear_velocity, hit_details.normal1) * hit_details.normal1; if input.jump { - velocity.linvel.y = controller.jump_speed; + velocity.linear.y = controller.jump_speed; } } @@ -383,17 +383,17 @@ pub fn fps_controller_move( wish_direction, wish_speed, controller.air_acceleration, - velocity.linvel, + velocity.linear, dt, ); add.y = -controller.gravity * dt; - velocity.linvel += add; + velocity.linear += add; - let air_speed = velocity.linvel.xz().length(); + let air_speed = velocity.linear.xz().length(); if air_speed > controller.max_air_speed { let ratio = controller.max_air_speed / air_speed; - velocity.linvel.x *= ratio; - velocity.linvel.z *= ratio; + velocity.linear.x *= ratio; + velocity.linear.z *= ratio; } } @@ -428,7 +428,7 @@ pub fn fps_controller_move( { // Try putting the player forward, but instead lifted upward by the step offset // If we can find a surface below us, we can adjust our position to be on top of it - let future_position = transform.translation + velocity.linvel * dt; + let future_position = transform.translation + velocity.linear * dt; let future_position_lifted = future_position + Vec3::Y * controller.step_offset; let cast = physics_context.single().unwrap().cast_shape( future_position_lifted, @@ -458,11 +458,11 @@ pub fn fps_controller_move( &collider, transform.as_ref(), &physics_context, - velocity.linvel, + velocity.linear, dt, ); if let Some(overhang) = overhang { - velocity.linvel -= overhang; + velocity.linear -= overhang; } } // If we are still overhanging consider unsolvable and freeze @@ -471,12 +471,12 @@ pub fn fps_controller_move( &collider, transform.as_ref(), &physics_context, - velocity.linvel, + velocity.linear, dt, ) .is_some() { - velocity.linvel = Vec3::ZERO; + velocity.linear = Vec3::ZERO; } } } @@ -552,7 +552,7 @@ fn overhang_component( let cast = physics_context.single().unwrap().cast_ray( future_position + Vec3::Y * 0.125, -Vec3::Y, - 0.375.into(), + 0.375_f32.into(), false, filter, );