-
-
Notifications
You must be signed in to change notification settings - Fork 289
Closed
Labels
Description
as the title says, i noticed that the CharacterController (3D) can move through a cuboid collider at a very specific velocities, that being normalized integer velocities. For example: Vec3::new(1.0, -5.0, -1.0).normalize()
. Even a slight deviation makes this impossible. I also noticed that varying the size of the collider has some impact.
here is the code with which i tested this bug. I spawned many shapes to get a better random sampling, but it also works with just one shape
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
const START_POSITION: Vec3 = Vec3::new(0., 1.5, 0.);
fn main() {
let mut app = App::new();
app.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(),
))
.insert_resource(ClearColor(Color::srgb(0.2, 0.2, 0.2)))
.add_systems(Startup, (spawn_basic_scene, spawn_shapes))
.add_systems(FixedUpdate, update_cubes)
.run();
}
#[derive(Component)]
struct Velocity(Vec3);
impl Velocity {
pub fn random() -> Self {
Velocity(
// truly random velocity -> i found no matching vel that way
// Vec3::new(
// fastrand::f32() * 2. - 1.,
// -fastrand::f32(),
// fastrand::f32() * 2. - 1.,
// )
// .normalize(),
// this velocity always works
// Vec3::new(1.0, -5.0, -1.0).normalize(),
// integer velocities
Vec3::new(
fastrand::i32(-6..=6) as f32,
fastrand::i32(-6..0) as f32,
fastrand::i32(-6..=6) as f32,
)
.normalize(),
// even a slight deviation from an integer velocity makes it very unlikely
// (Vec3::new(
// fastrand::i32(-6..=6) as f32,
// fastrand::i32(-6..0) as f32,
// fastrand::i32(-6..=6) as f32,
// )
// .normalize()
// + Vec3::new(
// fastrand::f32() * 2. - 1.,
// -fastrand::f32(),
// fastrand::f32() * 2. - 1.,
// )
// .normalize()
// * 0.00001)
// .normalize(),
)
}
}
fn spawn_shapes(mut commands: Commands) {
let group = CollisionGroups::new(Group::GROUP_1, Group::ALL ^ Group::GROUP_1);
// every shape except cuboid works
for shape in [
Collider::cuboid(0.5, 0.5, 0.5),
Collider::ball(0.5),
Collider::capsule_y(0.5, 0.2),
Collider::cylinder(0.5, 0.5),
] {
// i spawned many shapes to get a better random sampling, but it also works with just one
for _ in 0..100 {
commands.spawn((
group,
Velocity::random(),
RigidBody::KinematicPositionBased,
shape.clone(),
SpatialBundle {
transform: Transform::from_translation(START_POSITION),
..default()
},
KinematicCharacterController {
filter_groups: Some(group),
slide: true, // sliding seems to make no difference
..KinematicCharacterController::default()
},
));
}
}
}
fn spawn_basic_scene(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// spawn light
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(0., 5., 0.),
..default()
});
// spawn ground plane
commands.spawn((
// the size of the cuboid seems very important
Collider::cuboid(10., 0.1, 10.),
PbrBundle {
mesh: meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(10.)).mesh()),
material: materials.add(StandardMaterial {
base_color: Color::srgb(0.2, 0.9, 0.2),
..default()
}),
..default()
},
));
// add camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(10., 1.8, 0.).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}
fn update_cubes(
time: Res<Time>,
mut cubes: Query<(
&mut KinematicCharacterController,
&mut Velocity,
&mut Transform,
&Collider,
)>,
) {
for (mut controller, mut velocity, mut transform, collider) in &mut cubes {
let penetrated = transform.translation.y < -0.5;
if penetrated || transform.translation.distance_squared(Vec3::ZERO) > 9. {
if penetrated {
println!(
" -> {:?}: {:?}, speed = {:?}",
collider,
velocity.0,
velocity.0.length()
);
}
*transform = Transform::from_translation(START_POSITION);
*velocity = Velocity::random();
continue;
}
controller.translation = Some(velocity.0 * time.delta_seconds());
}
}