#Simple rectangle collision prevention

1 messages · Page 1 of 1 (latest)

sweet thistle
#

I'm trying to very basic collision detection working where I prevent one sprite controlled by the player from moving through the other. Avian seemed like a good choice for this so I've brought it in however I'm unclear what the correct approach is to actually preventing collision with is. This is what I've got:

use avian2d::prelude::*;
use bevy::prelude::*;

#[derive(Component)]
struct Player;

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

    commands.spawn((
        Player,
        SpriteBundle {
            sprite: Sprite {
                custom_size: Some(Vec2::new(100.0, 100.0)),
                ..default()
            },

            ..default()
        },
        RigidBody::Kinematic,
        Collider::round_rectangle(90., 90., 10.),
    ));

    commands.spawn((
        SpriteBundle {
            sprite: Sprite {
                custom_size: Some(Vec2::new(500.0, 100.0)),
                ..default()
            },
            transform: Transform::from_xyz(0., -200., 0.),
            ..default()
        },
        RigidBody::Static,
        Collider::round_rectangle(490., 90., 10.),
    ));
}

fn character_movement(
    mut characters: Query<(&mut Transform, &Sprite, &Player)>,
    input: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
) {
    for (mut transform, _, _) in &mut characters {
        if input.pressed(KeyCode::KeyW) {
            transform.translation.y += 100.0 * time.delta_seconds();
        }
        if input.pressed(KeyCode::KeyS) {
            transform.translation.y -= 100.0 * time.delta_seconds();
        }
        /* etc.... */
    }
}

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            PhysicsPlugins::default(),
            PhysicsDebugPlugin::default(),
        ))
        .add_systems(Startup, setup)
        .add_systems(Update, character_movement)
        .run();
}

#

Should I be listening for a collision event and then moving the player entity back out somehow? Should I be detecting collisions prior to movement and prevent movent that would cause one? Keen to understand what the most straightforward approach here is whilst still following good practices 🙂

hearty grove
#

If your character is a kinematic rigid body, you're getting into the territory of Kinematic Character Controllers. This is actually not trivial at all to solve. Making the rigid body of the characters Dynamic makes this much simpler. In that case, you apply impulses to the characters to move them around and the physics engine makes sure nothing ends up penetrating. If you create a dynamic character controller, you may also be interested in just using an established solution, namely Tnua

If you really really want to use kinematic bodies, the go-to algorithm to implement is called collide-and-slide. Here is a good introduction to it, but beware that the code presented comes with its own set of shortcomings because, as I said, this kind of stuff is really hard to get right.

sweet thistle
#

I had come across Tnua however it looked to me that it was primarily built for side-on type games, where as I'm looking to build something that will end up top down?

hearty grove
#

Look into Avian's ExternalImpulse. It gets inserted into RigidBody entities automatically.

#

Simply query for it and apply your impulse on it

sweet thistle
#

will that not give it a momentum?

hearty grove
#

If that is a problem, you can also set the LinearVelocity directly instead

sweet thistle
#

Ok, then presumably I need to manually zero it each update in the case where there is no input in order to avoid momentum?

#

hmmm doing that causes it to move extremely slowly

#

ok, adding a couple of orders of magnitude to the time delta multiple fixed that 🙂

#

Now when I move my box straight down into the static one it does collide however if I keep the button held down it starts moving very slightly to the side, is there a way to avoid this?

hearty grove
#

Ah, I meant you can set the velocity instead of the impulse

#

Just set a right-facing velocity when you press right, etc.

#

And yes, zero it beforehand 🙂

hearty grove
#

Since the velocity (and impulse) are already in SI units, you should not multiply them by dt

#

Avian already does its frame and time calculation in a fixed timestep

#

E.g. setting the linear velocity to Vec3::X * 100.0 is already enough to move 100 pixels per second to the right

wheat turret
sweet thistle
#

Added LinearDamping, however this seems to mean that there's now a small "bounce" back from the collision, what's the best way to avoid that?