jcornaz / heron

[DISCONTINUED] An ergonomic physics API for bevy games
MIT License
292 stars 44 forks source link

Continuous Collision Detection support #199

Open Kerollmops opened 2 years ago

Kerollmops commented 2 years ago

I am currently using heron for the Bevy Jam #1 and other projects too, I like it a lot.

But I am spawning some fast-moving entities in the world and was wondering if there were any way to enable the continuous-collision-detection options on a rigid body. I remember something in the heron documentation about being able to call raw rapier methods, am I right?

IIRC, it looks highly related to https://github.com/jcornaz/heron/issues/43.

jcornaz commented 2 years ago

There is no "first-class" abstraction for that.

But you may retrieve and mutate the rapier rigid body that is in the RigidBodySet resource via the RigidBodyHandle that is inserted in entities.

Example:

// Treat the following as pseudo-code, I didn't check if it would even compile!
fn enable_ccd(mut rigid_bodies: ResMut<RigidBodySet>, new_handles: Query<&RigidBodyHandle, Added<RigidBodyHandle>>) {
  for handle in new_handles.iter() {
    if let Some(body) = rigid_bodies.get_mut(handle.into_rapier()) {
       body.enable_ccd(true);
    }
  }
}

I leave this issue open as a request to add a "first-class" abstraction for enabling CCD. I guess the simplest approach would be to provide a CCD compnent. Inserting the component would enable CCD for the body, Removing the component would disable it.

jakoschiko commented 2 years ago

Thanks! This code compiles and solves the problem:

use bevy::prelude::*;
use heron::{
    prelude::*,
    rapier_plugin::{convert::IntoRapier, rapier2d::prelude::RigidBodySet, RigidBodyHandle},
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)]
pub enum Ccd {
    Disabled,
    Enabled,
}

impl Default for Ccd {
    fn default() -> Self {
        Self::Disabled
    }
}

pub struct PhysicsExtensionPlugin;

impl Plugin for PhysicsExtensionPlugin {
    fn build(&self, app: &mut App) {
        app.add_system_to_stage(CoreStage::First, update_ccd_for_body);
    }
}

fn update_ccd_for_body(
    mut rigid_bodies: ResMut<RigidBodySet>,
    new_handles: Query<(&RigidBodyHandle, &Ccd), Or<(Added<RigidBodyHandle>, Changed<Ccd>)>>,
) {
    for (&handle, &ccd) in new_handles.iter() {
        if let Some(body) = rigid_bodies.get_mut(handle.into_rapier()) {
            let enable = match ccd {
                Ccd::Enabled => true,
                Ccd::Disabled => false,
            };
            body.enable_ccd(enable);
        }
    }
}

Edit: Added the Or<...> because the Changed<Ccd> event can get lost for new bodies if heron is used with a fixed timestep.