dimforge / bevy_rapier

Official Rapier plugin for the Bevy game engine.
https://rapier.rs
Apache License 2.0
1.25k stars 261 forks source link

Collider `set_scale` does not work. #452

Open andriyDev opened 11 months ago

andriyDev commented 11 months ago

Setting the scale of a collider does not actually change its collider. Printing Collider::raw does show the collider being scaled correctly, but it's as if the actual physics and the debug render use the unscaled collider. To be clear, I'm testing collisions with a kinematic rigidbody (the example doesn't show this).

Example:

use bevy::{prelude::*, render::camera::ScalingMode};
use bevy_rapier2d::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(350.0))
        .add_plugins(RapierDebugRenderPlugin::default())
        .add_systems(Startup, spawn_collider)
        .run();
}

fn spawn_collider(mut commands: Commands) {
    let collider = Collider::convex_hull(&[
        Vec2::new(1.0, -1.0),
        Vec2::new(0.0, 1.0),
        Vec2::new(-1.0, -1.0),
    ])
    .unwrap();

    let mut scaled_collider = collider.clone();
    scaled_collider.set_scale(Vec2::ONE * 350.0, /* num_subdivisions= */ 1);
    commands
        .spawn(SpatialBundle::default())
        .insert(scaled_collider);

    commands.spawn(Camera2dBundle {
        projection: OrthographicProjection {
            near: -1000.0,
            far: 1000.0,
            scaling_mode: ScalingMode::FixedVertical(700.0),
            ..Default::default()
        },
        ..Default::default()
    });
}

Expected behavior: The scaled collider covers most of the screen. Actual behavior: The scaled collider is tiny.

Manually updating the vertices to be scaled by 350.0 results in the correct output, but that is much less convenient than just using set_scale().

Jondolf commented 10 months ago

Colliders are scaled by transform scale, so it's probably setting it back to Vec3::ONE based on your SpatialBundle, which is how I'd expect it to work.

Use transform scale for scaling the actual colliders used for physics entities. You can use set_scale if you need scaled colliders in some logic though (for e.g. shapecasts)

andriyDev commented 10 months ago

That makes sense I suppose except then why is set_scale public? If bevy_rapier is the one in control of this value, it should be private.

That said I'd rather have the ability to scale the collider independently because scaling transforms is usually a bad idea IMO. It can lead to shearing and stuff. So only scaling the collider would be nice.

Whichever works though!

Edit: oh wait I didn't read the full message, my bad. I see! I still think that scaling colliders independently seems nice. Alternatively, just adding a comment to set_scale that it'll be set for transforms also works.

Jondolf commented 10 months ago

Yeah, I think it could be documented better. Or maybe even change it to a scaled method that returns a new scaled version instead of mutating the original one.

I think scaling physics entities with transforms is basically the standard in game engines. For example, if you were using Unity, Godot, or Unreal, you probably wouldn't set the scale in code, but instead just change the transform via the GUI.

I'm pretty sure shearing does not cause issues in the case of scaling colliders. The scale is just a vector that scales the raw dimensions of a collider without doing any affine transformations or rotation. You should be able to notice this by rotating the child of a scaled entity and seeing that the visual mesh (with shear) doesn't match the collider (without shear). Haven't tested in rapier though! Shearing also only happens when a child entity of a scaled parent is rotated, which is probably quite rare.

You can scale the collider independently of the mesh by making it a child entity and scaling just that.