Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Incorrect normals when ray-casting using cast_shape #372

Open
AntonPieper opened this issue May 9, 2023 · 4 comments
Open

[Bug]: Incorrect normals when ray-casting using cast_shape #372

AntonPieper opened this issue May 9, 2023 · 4 comments
Labels
A-Integration very bevy specific D-Easy P-Low question Further information is requested S-not-started Work has not started

Comments

@AntonPieper
Copy link

I am trying to write a character controller using rapier. To control when I can jump, I want to use cast_shape to check whether I am currently grounded. This leads to a problem: the resulting normal is always Vec2(-0.0, 1.0) wven if I hit walls.

The relevant code of the system:

fn jump_controller(
    mut players: Query<(Entity, &mut Velocity, &Transform, &Collider), With<PlayerTag>>,
    keys: Res<Input<KeyCode>>,
    physics: Res<RapierContext>,
) {
    for (entity, mut velocity, transform, collider) in &mut players {
        let grounded = physics
            .cast_shape(
                transform.translation.truncate(),
                0.,
                Vec2::NEG_Y,
                collider,
                0.01,
                QueryFilter::new().exclude_collider(entity),
            )
            .is_some_and(|(_, collisions)| {
                println!("{:?}", collisions);
                (0.9..1.1).contains(&collisions.normal2.dot(Vec2::NEG_Y))
            });
        let mut movement = Vec2::default();
        if keys.pressed(KeyCode::Space) && grounded {
            movement.y += 200.;
        }
        velocity.linvel += movement;
    }
}
Full code (main.rs)

Here is my example code (important parts at lines 101 - 114 in the jump_controller function):

use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use rand::prelude::*;

#[derive(Bundle)]
struct Enemy {
    #[bundle]
    sprite: SpriteBundle,
    collider: Collider,
}

#[derive(Component)]
struct PlayerTag;
#[derive(Component)]
struct EnemyTag;

const START_X: i32 = -10;
const START_Y: i32 = 5;
const END_Y: i32 = 20;
const END_X: i32 = 10;

fn create_player(mut commands: Commands) {
    commands
        .spawn(RigidBody::Dynamic)
        .insert(Collider::cuboid(5., 5.))
        .insert(SpriteBundle {
            sprite: Sprite {
                color: Color::rgb(0.25, 0.25, 0.75),
                custom_size: Some(Vec2::new(10., 10.)),
                ..default()
            },
            ..default()
        })
        .insert(Velocity::default())
        .insert(LockedAxes::ROTATION_LOCKED)
        .insert(GravityScale(1.))
        .insert(Ccd::enabled())
        .insert(PlayerTag {});
}

fn create_camera(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());
}

fn create_enemies(mut commands: Commands) {
    for y in START_Y..END_Y {
        for x in START_X..END_X {
            commands
                .spawn(SpriteBundle {
                    transform: Transform::from_translation(Vec3::new(
                        x as f32 * 21.,
                        y as f32 * 21.,
                        0.,
                    ))
                    .with_scale(Vec3::new(20., 20., 1.)),
                    sprite: Sprite {
                        color: Color::hsl(rand::thread_rng().gen_range(0. ..=360.), 1., 0.5),
                        ..default()
                    },
                    ..default()
                })
                .insert(Collider::cuboid(0.5, 0.5))
                .insert(EnemyTag {});
        }
    }
}
fn create_ground(mut commands: Commands) {
    commands
        .spawn(SpriteBundle {
            sprite: Sprite {
                color: Color::Rgba {
                    red: 0.5,
                    green: 0.5,
                    blue: 0.5,
                    alpha: 1.,
                },
                ..default()
            },
            transform: Transform::from_translation(Vec3::new(0., -100., 0.))
                .with_scale(Vec3::new(1000., 100., 1.)),
            ..default()
        })
        .insert(Collider::cuboid(0.5, 0.5));
}

fn walk_controller(mut players: Query<&mut Velocity, With<PlayerTag>>, keys: Res<Input<KeyCode>>) {
    for mut velocity in &mut players {
        let mut movement = Vec2::default();
        movement.x -= if keys.pressed(KeyCode::A) { 1. } else { 0. };
        movement.x += if keys.pressed(KeyCode::D) { 1. } else { 0. };
        movement = movement.normalize_or_zero();
        velocity.linvel += movement;
    }
}
fn jump_controller(
    mut players: Query<(Entity, &mut Velocity, &Transform, &Collider), With<PlayerTag>>,
    keys: Res<Input<KeyCode>>,
    physics: Res<RapierContext>,
) {
    for (entity, mut velocity, transform, collider) in &mut players {
        let grounded = physics
            .cast_shape(
                transform.translation.truncate(),
                0.,
                Vec2::NEG_Y,
                collider,
                0.01,
                QueryFilter::new().exclude_collider(entity),
            )
            .is_some_and(|(_, collisions)| {
                println!("{:?}", collisions);
                (0.9..1.1).contains(&collisions.normal2.dot(Vec2::NEG_Y))
            });
        let mut movement = Vec2::default();
        if keys.pressed(KeyCode::Space) && grounded {
            movement.y += 200.;
        }
        velocity.linvel += movement;
    }
}

fn camera_controller(
    mut query: Query<&mut Transform, (With<Camera>, Without<PlayerTag>)>,
    player: Query<&Transform, With<PlayerTag>>,
) {
    for mut camera_transform in &mut query {
        camera_transform.translation = player.single().translation;
    }
}

pub struct GamePlugin;

impl Plugin for GamePlugin {
    fn build(&self, app: &mut App) {
        app.add_startup_systems((create_enemies, create_player, create_camera, create_ground))
            .add_systems((
                walk_controller,
                jump_controller,
                camera_controller.after(jump_controller),
            ));
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
        .add_plugin(RapierDebugRenderPlugin::default())
        .add_plugin(GamePlugin)
        .run();
}
My Cargo config

Cargo.toml

[package]
name = "animation-programming"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bevy = { version = "0.10.1" }
rand = "0.8.5"
bevy_rapier2d = { version = "0.21.0", features = [ "simd-nightly", "debug-render-2d" ] }

# Enable a small amount of optimization in debug mode
[profile.dev]
# opt-level = 1

[profile.release]
lto = "thin"

# Enable high optimizations for dependencies (incl. Bevy), but not for our code:
[profile.dev.package."*"]
opt-level = 3

rust-toolchain.toml

[toolchain]
channel = "nightly"
@Aceeri
Copy link
Contributor

Aceeri commented Jul 25, 2023

Be careful to check the toi.status for TOIStatus::Penetrating here, the results are currently undefined if it is. Ideally I think we hide the Toi stuff behind an enum so you have to check the status, but that's on my TODO list currently.

@Aceeri
Copy link
Contributor

Aceeri commented Aug 16, 2023

Also be careful to check the normal1 vs normal2 of the Toi, I'm not entirely sure if normal2 is actually the hit collider.

@Vrixyz Vrixyz added question Further information is requested D-Easy P-Low S-not-started Work has not started A-Integration very bevy specific labels May 23, 2024
@Vrixyz
Copy link
Contributor

Vrixyz commented May 23, 2024

@AntonPieper Is that still an issue for you with the information provided by Aceeri ?

@AntonPieper
Copy link
Author

I have moved on since then and haven't yet started another project again using bevy, so I don't know.

I can check this again this weekend though. Thanks for the valuable information!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Integration very bevy specific D-Easy P-Low question Further information is requested S-not-started Work has not started
Projects
None yet
Development

No branches or pull requests

3 participants