Skip to content
This repository has been archived by the owner on Nov 29, 2022. It is now read-only.

feat: add configurable StagedPhysicsPlugin #171

Closed
wants to merge 16 commits into from
22 changes: 15 additions & 7 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,22 @@ pub enum PhysicsSystem {
/// Plugin that registers stage resources and components.
///
/// It does **NOT** enable physics behavior.
#[derive(Debug, Copy, Clone, Default)]
pub struct CorePlugin;
#[derive(Debug, Copy, Clone)]
pub struct CorePlugin<StepStage: StageLabel + Clone> {
/// The stage to run [`PhysicsSteps::update`] to tick the physics system timer
pub step_stage: StepStage,
}

impl Default for CorePlugin<CoreStage> {
fn default() -> Self {
Self {
step_stage: CoreStage::First,
}
}
}

#[allow(deprecated)]
impl Plugin for CorePlugin {
impl<StepStage: StageLabel + Clone> Plugin for CorePlugin<StepStage> {
fn build(&self, app: &mut App) {
app.init_resource::<Gravity>()
.init_resource::<PhysicsTime>()
Expand All @@ -72,10 +83,7 @@ impl Plugin for CorePlugin {
.register_type::<RotationConstraints>()
.register_type::<CollisionLayers>()
.register_type::<SensorShape>()
.add_system_to_stage(CoreStage::First, PhysicsSteps::update.system())
.add_stage_before(CoreStage::PostUpdate, crate::stage::ROOT, {
Schedule::default().with_stage(crate::stage::UPDATE, SystemStage::parallel())
});
jcornaz marked this conversation as resolved.
Show resolved Hide resolved
.add_system_to_stage(self.step_stage.clone(), PhysicsSteps::update);
}
}

Expand Down
95 changes: 95 additions & 0 deletions examples/custom_schedule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::f32::consts::PI;

use bevy::prelude::*;

use heron::*;

fn main() {
let physics_schedule = "Physics-stage";
let post_physics_stage = CoreStage::PostUpdate;
let step_physics_stage = CoreStage::First;
App::new()
// need to setup the custom physics schedule
.add_stage_before(CoreStage::Update, physics_schedule, Schedule::default())
.add_plugins(DefaultPlugins)
.add_plugin(StagedPhysicsPlugin::new(
physics_schedule,
post_physics_stage,
step_physics_stage,
)) // Add the plugin
.insert_resource(Gravity::from(Vec2::new(0.0, -600.0))) // Define the gravity
.add_startup_system(spawn)
.add_system(log_collisions)
.run();
}

fn spawn(mut commands: Commands) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());

// The ground
let size = Vec2::new(1000.0, 50.0);
commands
// Spawn a bundle that contains at least a `GlobalTransform`
.spawn_bundle(SpriteBundle {
sprite: Sprite {
color: Color::WHITE,
custom_size: Some(size),
..Default::default()
},
transform: Transform::from_translation(Vec3::new(0.0, -300.0, 0.0)),
..Default::default()
})
// Make it a rigid body
.insert(RigidBody::Static)
// Attach a collision shape
.insert(CollisionShape::Cuboid {
half_extends: size.extend(0.0) / 2.0,
border_radius: None,
})
// Define restitution (so that it bounces)
.insert(PhysicMaterial {
restitution: 0.5,
..Default::default()
});

// The Ball
let size = Vec2::new(30.0, 30.0);
commands
// Spawn a bundle that contains at least a `GlobalTransform`
.spawn_bundle(SpriteBundle {
sprite: Sprite {
color: Color::GREEN,
custom_size: Some(size),
..Default::default()
},
transform: Transform::from_translation(Vec3::new(-400.0, 200.0, 0.0)),
..Default::default()
})
// Make it a rigid body
.insert(RigidBody::Dynamic)
// Attach a collision shape
.insert(CollisionShape::Cuboid {
half_extends: size.extend(0.0) / 2.0,
border_radius: None,
})
// Add an initial velocity. (it is also possible to read/mutate this component later)
.insert(Velocity::from(Vec2::X * 300.0).with_angular(AxisAngle::new(Vec3::Z, -PI)))
// Define restitution (so that it bounces)
.insert(PhysicMaterial {
restitution: 0.7,
..Default::default()
});
}

fn log_collisions(mut events: EventReader<CollisionEvent>) {
for event in events.iter() {
match event {
CollisionEvent::Started(d1, d2) => {
println!("Collision started between {:?} and {:?}", d1, d2)
}
CollisionEvent::Stopped(d1, d2) => {
println!("Collision stopped between {:?} and {:?}", d1, d2)
}
}
}
}
82 changes: 57 additions & 25 deletions rapier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,29 @@ mod velocity;

/// Plugin that enables collision detection and physics behavior, powered by rapier.
#[must_use]
#[derive(Debug, Copy, Clone, Default)]
pub struct RapierPlugin;
#[derive(Debug, Copy, Clone)]
pub struct RapierPlugin<
PhysicsSchedule: StageLabel + Clone,
PostPhysicsStage: StageLabel + Clone,
StepStage: StageLabel + Clone,
> {
/// The stage where heron will run rapier physics logic
pub physics_schedule: PhysicsSchedule,
/// The stage where heron will update bevy components based on the rapier physics results
pub post_physics_stage: PostPhysicsStage,
/// The stage to run [`heron_core::step::PhysicsSteps::update`] to tick the physics system timer
pub step_physics_stage: StepStage,
}

impl Default for RapierPlugin<&'static str, CoreStage, CoreStage> {
fn default() -> Self {
Self {
physics_schedule: "heron-physics",
post_physics_stage: CoreStage::PostUpdate,
step_physics_stage: CoreStage::First,
}
}
}

/// Component that holds a reference to the rapier rigid body
///
Expand All @@ -70,30 +91,41 @@ enum InternalSystem {
TransformPropagation,
}

impl Plugin for RapierPlugin {
impl<
PhysicsSchedule: StageLabel + Clone,
PostPhysicsStage: StageLabel + Clone,
StepStage: StageLabel + Clone,
> Plugin for RapierPlugin<PhysicsSchedule, PostPhysicsStage, StepStage>
{
fn build(&self, app: &mut App) {
app.add_plugin(heron_core::CorePlugin)
.init_resource::<PhysicsPipeline>()
.init_resource::<body::HandleMap>()
.init_resource::<shape::HandleMap>()
.init_resource::<IntegrationParameters>()
.add_event::<CollisionEvent>()
.insert_resource(BroadPhase::new())
.insert_resource(NarrowPhase::new())
.insert_resource(RigidBodySet::new())
.insert_resource(QueryPipeline::new())
.insert_resource(IslandManager::new())
.insert_resource(ColliderSet::new())
.insert_resource(JointSet::new())
.insert_resource(CCDSolver::new())
.stage("heron-physics", |schedule: &mut Schedule| {
schedule
.add_stage("heron-remove", removal_stage())
.add_stage("heron-update-rapier-world", update_rapier_world_stage())
.add_stage("heron-create-new-bodies", body_update_stage())
.add_stage("heron-create-new-colliders", create_collider_stage())
})
.add_system_set_to_stage(CoreStage::PostUpdate, step_systems());
app.add_plugin(heron_core::CorePlugin {
step_stage: self.step_physics_stage.clone(),
})
.init_resource::<PhysicsPipeline>()
.init_resource::<body::HandleMap>()
.init_resource::<shape::HandleMap>()
.init_resource::<IntegrationParameters>()
.add_event::<CollisionEvent>()
.insert_resource(BroadPhase::new())
.insert_resource(NarrowPhase::new())
.insert_resource(RigidBodySet::new())
.insert_resource(QueryPipeline::new())
.insert_resource(IslandManager::new())
.insert_resource(ColliderSet::new())
.insert_resource(JointSet::new())
.insert_resource(CCDSolver::new())
.add_system_set_to_stage(self.post_physics_stage.clone(), step_systems());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this approach will work for something like bevy_ggrs (yet). That library impl Stage for GGRSStage, which it uses to handle updating game state with ggrs, which it adds before CoreStage::Update. Unfortunately, add_system_set_to_stage only works for SystemStages. This means, specifically for bevy_ggrs at least, that bevy function will not find the GGRSStage and add the desired step systems.

That's not to say this PR doesn't work, just that it will not work with other plugins that implement custom stages (i.e., bevy_ggrs). (I am guessing bevy_backroll also falls into this category, but I have not read much, going by this impl Stage)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Not sure if we even can do anything for it until stageless.


let physics_schedule = app
.schedule
.get_stage_mut::<Schedule>(&self.physics_schedule)
.expect("The provided physics_schedule was not found.");

physics_schedule
.add_stage("heron-remove", removal_stage())
.add_stage("heron-update-rapier-world", update_rapier_world_stage())
.add_stage("heron-create-new-bodies", body_update_stage())
.add_stage("heron-create-new-colliders", create_collider_stage());
}
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ mod tests {

let mut app = App::new();
app.add_plugins(MinimalPlugins)
.add_plugin(RapierPlugin)
.add_plugin(RapierPlugin::default())
.add_startup_system(setup.system());

app
Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/acceleration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/bodies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/body_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/children_collision_shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());

builder
}
Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/damping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn test_app() -> App {
app.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
app
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/density.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin)
.add_plugin(RapierPlugin::default())
.add_system_to_stage(
bevy::app::CoreStage::PostUpdate,
bevy::transform::transform_propagate_system::transform_propagate_system.system(),
Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/friction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/restitution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/sensor_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
2 changes: 1 addition & 1 deletion rapier/tests/velocity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn test_app() -> App {
.init_resource::<TypeRegistryArc>()
.insert_resource(PhysicsSteps::every_frame(Duration::from_secs(1)))
.add_plugin(CorePlugin)
.add_plugin(RapierPlugin);
.add_plugin(RapierPlugin::default());
builder
}

Expand Down
Loading