diff --git a/README.md b/README.md index 4fe4caec..d6024ce2 100644 --- a/README.md +++ b/README.md @@ -69,9 +69,8 @@ use bevy_xpbd_3d::prelude::*; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugins(PhysicsPlugins) - .add_startup_system(setup) + .add_plugins((DefaultPlugins, PhysicsPlugins::default())) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_2d/Cargo.toml b/crates/bevy_xpbd_2d/Cargo.toml index 3de53217..555cba48 100644 --- a/crates/bevy_xpbd_2d/Cargo.toml +++ b/crates/bevy_xpbd_2d/Cargo.toml @@ -16,7 +16,7 @@ default = [ "2d", "f32", "collider-from-mesh" ] 2d = [] f32 = [ "dep:parry2d" ] f64 = [ "dep:parry2d-f64" ] -debug-plugin = [ "dep:bevy_prototype_debug_lines", "examples_common_2d/debug-plugin" ] +debug-plugin = [ "bevy/bevy_gizmos" ] simd = [ "parry2d?/simd-stable", "parry2d-f64?/simd-stable" ] enhanced-determinism = [ "parry2d?/enhanced-determinism", "parry2d-f64?/enhanced-determinism" ] collider-from-mesh = [ "bevy/bevy_render" ] @@ -28,17 +28,16 @@ required-features = [ "2d" ] [dependencies] bevy_xpbd_derive = { path = "../bevy_xpbd_derive", version = "0.1" } -bevy = { version = "0.10.1", default-features = false } -bevy_prototype_debug_lines = { version = "0.10.1", optional = true } -parry2d = { version = "0.13.1", optional = true } -parry2d-f64 = { version = "0.13.1", optional = true } -nalgebra = { version = "0.32.2", features = [ "convert-glam023" ] } +bevy = { version = "0.11", default-features = false } +parry2d = { version = "0.13", optional = true } +parry2d-f64 = { version = "0.13", optional = true } +nalgebra = { version = "0.32", features = [ "convert-glam024" ] } derive_more = "0.99" [dev-dependencies] examples_common_2d = { path = "../examples_common_2d" } approx = "0.5" -glam = { version = "0.23", features = [ "approx" ] } +glam = { version = "0.24", features = [ "approx" ] } insta = "1.0" itertools = "0.10" diff --git a/crates/bevy_xpbd_2d/examples/chain_2d.rs b/crates/bevy_xpbd_2d/examples/chain_2d.rs index 6039fb90..6fdfea24 100644 --- a/crates/bevy_xpbd_2d/examples/chain_2d.rs +++ b/crates/bevy_xpbd_2d/examples/chain_2d.rs @@ -6,13 +6,12 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(SubstepCount(50)) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) - .add_system(follow_mouse) + .add_systems(Startup, setup) + .add_systems(Update, follow_mouse) .run(); } diff --git a/crates/bevy_xpbd_2d/examples/collision_layers.rs b/crates/bevy_xpbd_2d/examples/collision_layers.rs index a5c7084f..8ce622c1 100644 --- a/crates/bevy_xpbd_2d/examples/collision_layers.rs +++ b/crates/bevy_xpbd_2d/examples/collision_layers.rs @@ -6,11 +6,10 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_2d/examples/fixed_joint_2d.rs b/crates/bevy_xpbd_2d/examples/fixed_joint_2d.rs index ec792c8a..dbcaf31b 100644 --- a/crates/bevy_xpbd_2d/examples/fixed_joint_2d.rs +++ b/crates/bevy_xpbd_2d/examples/fixed_joint_2d.rs @@ -4,12 +4,11 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(SubstepCount(50)) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_2d/examples/move_marbles.rs b/crates/bevy_xpbd_2d/examples/move_marbles.rs index 6d489a61..50e313b7 100644 --- a/crates/bevy_xpbd_2d/examples/move_marbles.rs +++ b/crates/bevy_xpbd_2d/examples/move_marbles.rs @@ -6,13 +6,12 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(SubstepCount(6)) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) - .add_system(movement) + .add_systems(Startup, setup) + .add_systems(Update, movement) .run(); } diff --git a/crates/bevy_xpbd_2d/examples/prismatic_joint_2d.rs b/crates/bevy_xpbd_2d/examples/prismatic_joint_2d.rs index c5aa987d..9bc31532 100644 --- a/crates/bevy_xpbd_2d/examples/prismatic_joint_2d.rs +++ b/crates/bevy_xpbd_2d/examples/prismatic_joint_2d.rs @@ -4,12 +4,11 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(SubstepCount(50)) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_2d/examples/ray_caster.rs b/crates/bevy_xpbd_2d/examples/ray_caster.rs index ee806822..c5ba3d58 100644 --- a/crates/bevy_xpbd_2d/examples/ray_caster.rs +++ b/crates/bevy_xpbd_2d/examples/ray_caster.rs @@ -7,15 +7,14 @@ use bevy::{prelude::*, sprite::MaterialMesh2dBundle}; use bevy_xpbd_2d::prelude::*; -use examples_common_2d::{bevy_prototype_debug_lines::*, *}; +use examples_common_2d::*; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) - .add_system(render_rays) - .add_startup_system(setup) + .add_systems(Update, render_rays) + .add_systems(Startup, setup) .run(); } @@ -58,27 +57,21 @@ fn setup( )); } -fn render_rays(mut rays: Query<(&mut RayCaster, &mut RayHits)>, mut lines: ResMut) { +fn render_rays(mut rays: Query<(&mut RayCaster, &mut RayHits)>, mut gizmos: Gizmos) { for (ray, hits) in &mut rays { // Convert to Vec3 for lines - let origin = ray.global_origin().extend(0.0).as_f32(); - let direction = ray.global_direction().extend(0.0).as_f32(); + let origin = ray.global_origin().as_f32(); + let direction = ray.global_direction().as_f32(); for hit in hits.iter() { - lines.line_colored( + gizmos.line_2d( origin, origin + direction * hit.time_of_impact as f32, - 0.001, Color::GREEN, ); } if hits.is_empty() { - lines.line_colored( - origin, - origin + direction * 1_000_000.0, - 0.001, - Color::ORANGE_RED, - ); + gizmos.line_2d(origin, origin + direction * 1_000_000.0, Color::ORANGE_RED); } } } diff --git a/crates/bevy_xpbd_2d/examples/revolute_joint_2d.rs b/crates/bevy_xpbd_2d/examples/revolute_joint_2d.rs index 2b202e1f..177c6289 100644 --- a/crates/bevy_xpbd_2d/examples/revolute_joint_2d.rs +++ b/crates/bevy_xpbd_2d/examples/revolute_joint_2d.rs @@ -4,12 +4,11 @@ use examples_common_2d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(SubstepCount(50)) .insert_resource(Gravity(Vector::NEG_Y * 1000.0)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_3d/Cargo.toml b/crates/bevy_xpbd_3d/Cargo.toml index 32531a81..c1153685 100644 --- a/crates/bevy_xpbd_3d/Cargo.toml +++ b/crates/bevy_xpbd_3d/Cargo.toml @@ -16,7 +16,7 @@ default = [ "3d", "f32", "collider-from-mesh" ] 3d = [] f32 = [ "dep:parry3d" ] f64 = [ "dep:parry3d-f64" ] -debug-plugin = [ "dep:bevy_prototype_debug_lines", "examples_common_3d/debug-plugin" ] +debug-plugin = [ "bevy/bevy_gizmos" ] simd = [ "parry3d?/simd-stable", "parry3d-f64?/simd-stable" ] enhanced-determinism = [ "parry3d?/enhanced-determinism", "parry3d-f64?/enhanced-determinism" ] collider-from-mesh = [ "bevy/bevy_render" ] @@ -28,17 +28,16 @@ required-features = [ "3d" ] [dependencies] bevy_xpbd_derive = { path = "../bevy_xpbd_derive", version = "0.1" } -bevy = { version = "0.10.1", default-features = false } -bevy_prototype_debug_lines = { version = "0.10.1", optional = true, features = [ "3d" ] } -parry3d = { version = "0.13.1", optional = true } -parry3d-f64 = { version = "0.13.1", optional = true } -nalgebra = { version = "0.32.2", features = [ "convert-glam023" ] } +bevy = { version = "0.11", default-features = false } +parry3d = { version = "0.13", optional = true } +parry3d-f64 = { version = "0.13", optional = true } +nalgebra = { version = "0.32", features = [ "convert-glam024" ] } derive_more = "0.99" [dev-dependencies] examples_common_3d = { path = "../examples_common_3d" } approx = "0.5" -glam = { version = "0.23", features = [ "approx" ] } +glam = { version = "0.24", features = [ "approx" ] } insta = "1.0" itertools = "0.10" diff --git a/crates/bevy_xpbd_3d/examples/chain_3d.rs b/crates/bevy_xpbd_3d/examples/chain_3d.rs index decef44b..27325cda 100644 --- a/crates/bevy_xpbd_3d/examples/chain_3d.rs +++ b/crates/bevy_xpbd_3d/examples/chain_3d.rs @@ -6,8 +6,7 @@ use examples_common_3d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Msaa::Sample4) .insert_resource(AmbientLight { @@ -16,8 +15,8 @@ fn main() { }) .insert_resource(SubstepCount(80)) .insert_resource(Gravity(Vector::NEG_Y * 9.81 * 2.0)) - .add_startup_system(setup) - .add_system(movement) + .add_systems(Startup, setup) + .add_systems(Update, movement) .run(); } diff --git a/crates/bevy_xpbd_3d/examples/cubes.rs b/crates/bevy_xpbd_3d/examples/cubes.rs index 4babb5a3..6197326d 100644 --- a/crates/bevy_xpbd_3d/examples/cubes.rs +++ b/crates/bevy_xpbd_3d/examples/cubes.rs @@ -6,12 +6,11 @@ use examples_common_3d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Msaa::Sample4) - .add_startup_system(setup) - .add_system(movement) + .add_systems(Startup, setup) + .add_systems(Update, movement) .run(); } @@ -45,9 +44,9 @@ fn setup( for y in -2..2 { for z in -2..2 { let pos = Vector::new( - x as Scalar * (cube_size + 0.1), - y as Scalar * (cube_size + 0.1), - z as Scalar * (cube_size + 0.1), + x as Scalar * (cube_size + 0.05), + y as Scalar * (cube_size + 0.05), + z as Scalar * (cube_size + 0.05), ); commands.spawn(( PbrBundle { diff --git a/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs b/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs index 3358a95d..f5f18339 100644 --- a/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs +++ b/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs @@ -11,13 +11,13 @@ fn main() { // Add PhysicsPlugins and replace default broad phase with our custom broad phase app.add_plugins( - PhysicsPlugins + PhysicsPlugins::default() .build() .disable::() .add(BruteForceBroadPhasePlugin), ); - app.add_startup_system(setup).run(); + app.add_systems(Startup, setup).run(); } // Modified from Bevy's 3d_scene example, a cube falling to the ground with velocity @@ -77,7 +77,7 @@ impl Plugin for BruteForceBroadPhasePlugin { .expect("add PhysicsSchedule first"); // Add the broad phase system into the broad phase set - physics_schedule.add_system(collect_collision_pairs.in_set(PhysicsSet::BroadPhase)); + physics_schedule.add_systems(collect_collision_pairs.in_set(PhysicsSet::BroadPhase)); } } diff --git a/crates/bevy_xpbd_3d/examples/custom_constraint.rs b/crates/bevy_xpbd_3d/examples/custom_constraint.rs index fadf866e..ef84fd16 100644 --- a/crates/bevy_xpbd_3d/examples/custom_constraint.rs +++ b/crates/bevy_xpbd_3d/examples/custom_constraint.rs @@ -5,15 +5,14 @@ fn main() { let mut app = App::new(); // Add plugins and startup system - app.add_plugins(DefaultPlugins) - .add_plugins(PhysicsPlugins) - .add_startup_system(setup); + app.add_plugins((DefaultPlugins, PhysicsPlugins::default())) + .add_systems(Startup, setup); // Get physics substep schedule and add our custom distance constraint let substeps = app .get_schedule_mut(SubstepSchedule) .expect("add SubstepSchedule first"); - substeps.add_system( + substeps.add_systems( solve_constraint::.in_set(SubstepSet::SolveUserConstraints), ); diff --git a/crates/bevy_xpbd_3d/examples/fixed_joint_3d.rs b/crates/bevy_xpbd_3d/examples/fixed_joint_3d.rs index 57c1d102..339089bc 100644 --- a/crates/bevy_xpbd_3d/examples/fixed_joint_3d.rs +++ b/crates/bevy_xpbd_3d/examples/fixed_joint_3d.rs @@ -4,12 +4,11 @@ use examples_common_3d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Msaa::Sample4) .insert_resource(SubstepCount(50)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_3d/examples/prismatic_joint_3d.rs b/crates/bevy_xpbd_3d/examples/prismatic_joint_3d.rs index d0f0dab5..fa15fd51 100644 --- a/crates/bevy_xpbd_3d/examples/prismatic_joint_3d.rs +++ b/crates/bevy_xpbd_3d/examples/prismatic_joint_3d.rs @@ -4,12 +4,11 @@ use examples_common_3d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Msaa::Sample4) .insert_resource(SubstepCount(50)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/bevy_xpbd_3d/examples/revolute_joint_3d.rs b/crates/bevy_xpbd_3d/examples/revolute_joint_3d.rs index 3ba2901a..0724abed 100644 --- a/crates/bevy_xpbd_3d/examples/revolute_joint_3d.rs +++ b/crates/bevy_xpbd_3d/examples/revolute_joint_3d.rs @@ -4,12 +4,11 @@ use examples_common_3d::XpbdExamplePlugin; fn main() { App::new() - .add_plugins(DefaultPlugins) - .add_plugin(XpbdExamplePlugin) + .add_plugins((DefaultPlugins, XpbdExamplePlugin)) .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1))) .insert_resource(Msaa::Sample4) .insert_resource(SubstepCount(50)) - .add_startup_system(setup) + .add_systems(Startup, setup) .run(); } diff --git a/crates/examples_common_2d/Cargo.toml b/crates/examples_common_2d/Cargo.toml index c938f0af..322c05c1 100644 --- a/crates/examples_common_2d/Cargo.toml +++ b/crates/examples_common_2d/Cargo.toml @@ -3,11 +3,8 @@ name = "examples_common_2d" version = "0.1.0" edition = "2021" -[features] -debug-plugin = [] - [dependencies] -bevy = { version = "0.10.1", default-features = false, features = [ +bevy = { version = "0.11", default-features = false, features = [ "bevy_core_pipeline", "bevy_text", "bevy_ui", @@ -15,9 +12,12 @@ bevy = { version = "0.10.1", default-features = false, features = [ "bevy_render", "bevy_sprite", "bevy_pbr", + "bevy_gizmos", + "default_font", + "tonemapping_luts", + "ktx2", + "zstd", "bevy_winit", "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland ] } -bevy_screen_diagnostics = "0.2" -bevy_prototype_debug_lines = "0.10.1" bevy_xpbd_2d = { path = "../bevy_xpbd_2d", default-features = false } diff --git a/crates/examples_common_2d/src/lib.rs b/crates/examples_common_2d/src/lib.rs index 43eb6688..59b166ae 100644 --- a/crates/examples_common_2d/src/lib.rs +++ b/crates/examples_common_2d/src/lib.rs @@ -1,8 +1,8 @@ -pub extern crate bevy_prototype_debug_lines; - -use bevy::prelude::*; -use bevy_prototype_debug_lines::DebugLinesPlugin; -use bevy_screen_diagnostics::{ScreenDiagnosticsPlugin, ScreenFrameDiagnosticsPlugin}; +use bevy::{ + diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}, + prelude::*, + text::DEFAULT_FONT_HANDLE, +}; use bevy_xpbd_2d::prelude::*; #[derive(Default)] @@ -10,18 +10,14 @@ pub struct XpbdExamplePlugin; impl Plugin for XpbdExamplePlugin { fn build(&self, app: &mut App) { - app.add_plugins(PhysicsPlugins) - .add_plugin(ScreenDiagnosticsPlugin::default()) - .add_plugin(ScreenFrameDiagnosticsPlugin) + app.add_plugins((PhysicsPlugins::default(), FrameTimeDiagnosticsPlugin)) .add_state::() - .add_system(bevy_xpbd_2d::pause.in_schedule(OnEnter(AppState::Paused))) - .add_system(bevy_xpbd_2d::resume.in_schedule(OnExit(AppState::Paused))) - .add_system(pause_button) - .add_system(step_button.run_if(in_state(AppState::Paused))); - #[cfg(not(feature = "debug-plugin"))] - { - app.add_plugin(DebugLinesPlugin::default()); - } + .add_systems(Startup, setup) + .add_systems(OnEnter(AppState::Paused), bevy_xpbd_2d::pause) + .add_systems(OnExit(AppState::Paused), bevy_xpbd_2d::resume) + .add_systems(Update, update_fps_text) + .add_systems(Update, pause_button) + .add_systems(Update, step_button.run_if(in_state(AppState::Paused))); } } @@ -38,7 +34,7 @@ fn pause_button( keys: Res>, ) { if keys.just_pressed(KeyCode::P) { - let new_state = match current_state.0 { + let new_state = match current_state.get() { AppState::Paused => AppState::Running, AppState::Running => AppState::Paused, }; @@ -51,3 +47,37 @@ fn step_button(mut physics_loop: ResMut, keys: Res>) physics_loop.step(); } } + +#[derive(Component)] +struct FpsText; + +fn setup(mut commands: Commands) { + commands.spawn(( + TextBundle::from_section( + "FPS: ", + TextStyle { + font: DEFAULT_FONT_HANDLE.typed(), + font_size: 20.0, + color: Color::TOMATO, + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(5.0), + left: Val::Px(5.0), + ..default() + }), + FpsText, + )); +} + +fn update_fps_text(diagnostics: Res, mut query: Query<&mut Text, With>) { + for mut text in &mut query { + if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) { + if let Some(value) = fps.smoothed() { + // Update the value of the second section + text.sections[0].value = format!("FPS: {value:.2}"); + } + } + } +} diff --git a/crates/examples_common_3d/Cargo.toml b/crates/examples_common_3d/Cargo.toml index 69103b40..fd954aed 100644 --- a/crates/examples_common_3d/Cargo.toml +++ b/crates/examples_common_3d/Cargo.toml @@ -3,11 +3,8 @@ name = "examples_common_3d" version = "0.1.0" edition = "2021" -[features] -debug-plugin = [] - [dependencies] -bevy = { version = "0.10.1", default-features = false, features = [ +bevy = { version = "0.11", default-features = false, features = [ "bevy_core_pipeline", "bevy_text", "bevy_ui", @@ -15,9 +12,12 @@ bevy = { version = "0.10.1", default-features = false, features = [ "bevy_render", "bevy_sprite", "bevy_pbr", + "bevy_gizmos", + "default_font", + "tonemapping_luts", + "ktx2", + "zstd", "bevy_winit", "x11", # github actions runners don't have libxkbcommon installed, so can't use wayland ] } -bevy_screen_diagnostics = "0.2" -bevy_prototype_debug_lines = "0.10.1" bevy_xpbd_3d = { path = "../bevy_xpbd_3d", default-features = false } diff --git a/crates/examples_common_3d/src/lib.rs b/crates/examples_common_3d/src/lib.rs index bb285524..365cacc0 100644 --- a/crates/examples_common_3d/src/lib.rs +++ b/crates/examples_common_3d/src/lib.rs @@ -1,8 +1,8 @@ -pub extern crate bevy_prototype_debug_lines; - -use bevy::prelude::*; -use bevy_prototype_debug_lines::DebugLinesPlugin; -use bevy_screen_diagnostics::{ScreenDiagnosticsPlugin, ScreenFrameDiagnosticsPlugin}; +use bevy::{ + diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}, + prelude::*, + text::DEFAULT_FONT_HANDLE, +}; use bevy_xpbd_3d::prelude::*; #[derive(Default)] @@ -10,18 +10,14 @@ pub struct XpbdExamplePlugin; impl Plugin for XpbdExamplePlugin { fn build(&self, app: &mut App) { - app.add_plugins(PhysicsPlugins) - .add_plugin(ScreenDiagnosticsPlugin::default()) - .add_plugin(ScreenFrameDiagnosticsPlugin) + app.add_plugins((PhysicsPlugins::default(), FrameTimeDiagnosticsPlugin)) .add_state::() - .add_system(bevy_xpbd_3d::pause.in_schedule(OnEnter(AppState::Paused))) - .add_system(bevy_xpbd_3d::resume.in_schedule(OnExit(AppState::Paused))) - .add_system(pause_button) - .add_system(step_button.run_if(in_state(AppState::Paused))); - #[cfg(not(feature = "debug-plugin"))] - { - app.add_plugin(DebugLinesPlugin::default()); - } + .add_systems(Startup, setup) + .add_systems(OnEnter(AppState::Paused), bevy_xpbd_3d::pause) + .add_systems(OnExit(AppState::Paused), bevy_xpbd_3d::resume) + .add_systems(Update, update_fps_text) + .add_systems(Update, pause_button) + .add_systems(Update, step_button.run_if(in_state(AppState::Paused))); } } @@ -38,7 +34,7 @@ fn pause_button( keys: Res>, ) { if keys.just_pressed(KeyCode::P) { - let new_state = match current_state.0 { + let new_state = match current_state.get() { AppState::Paused => AppState::Running, AppState::Running => AppState::Paused, }; @@ -51,3 +47,37 @@ fn step_button(mut physics_loop: ResMut, keys: Res>) physics_loop.step(); } } + +#[derive(Component)] +struct FpsText; + +fn setup(mut commands: Commands) { + commands.spawn(( + TextBundle::from_section( + "FPS: ", + TextStyle { + font: DEFAULT_FONT_HANDLE.typed(), + font_size: 20.0, + color: Color::TOMATO, + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(5.0), + left: Val::Px(5.0), + ..default() + }), + FpsText, + )); +} + +fn update_fps_text(diagnostics: Res, mut query: Query<&mut Text, With>) { + for mut text in &mut query { + if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) { + if let Some(value) = fps.smoothed() { + // Update the value of the second section + text.sections[0].value = format!("FPS: {value:.2}"); + } + } + } +} diff --git a/src/collision.rs b/src/collision.rs index 8256a553..9df4c506 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -3,15 +3,15 @@ use crate::prelude::*; /// A [collision event](Collider#collision-events) that is sent for each contact pair during the narrow phase. -#[derive(Clone, Debug, PartialEq)] +#[derive(Event, Clone, Debug, PartialEq)] pub struct Collision(pub Contact); /// A [collision event](Collider#collision-events) that is sent when two entities start colliding. -#[derive(Clone, Debug, PartialEq)] +#[derive(Event, Clone, Debug, PartialEq)] pub struct CollisionStarted(pub Entity, pub Entity); /// A [collision event](Collider#collision-events) that is sent when two entities stop colliding. -#[derive(Clone, Debug, PartialEq)] +#[derive(Event, Clone, Debug, PartialEq)] pub struct CollisionEnded(pub Entity, pub Entity); /// Data related to a contact between two bodies. diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index 0d4bd80f..30462886 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -73,7 +73,7 @@ //! .expect("add SubstepSchedule first"); //! //! // Add custom constraint -//! substeps.add_system( +//! substeps.add_systems( //! solve_constraint::.in_set(SubstepSet::SolveUserConstraints), //! ); //! ``` diff --git a/src/lib.rs b/src/lib.rs index b45ea14e..ad475cc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,8 +102,7 @@ //! //! fn main() { //! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_plugins(PhysicsPlugins) +//! .add_plugins((DefaultPlugins, PhysicsPlugins::default())) //! // ...your other plugins, systems and resources //! .run(); //! } diff --git a/src/plugins/debug.rs b/src/plugins/debug.rs index 93e29f05..c1b96557 100644 --- a/src/plugins/debug.rs +++ b/src/plugins/debug.rs @@ -4,7 +4,6 @@ use crate::prelude::*; use bevy::prelude::*; -use bevy_prototype_debug_lines::*; /// Renders physics objects and events like [AABBs](ColliderAabb) and [contacts](Collision) for debugging purposes. /// @@ -13,15 +12,23 @@ pub struct PhysicsDebugPlugin; impl Plugin for PhysicsDebugPlugin { fn build(&self, app: &mut App) { - app.add_plugin(DebugLinesPlugin::default()) - .init_resource::() + app.init_resource::() + .insert_resource(GizmoConfig { + line_width: 1.0, + ..default() + }) .register_type::() - .add_system( - debug_render_aabbs.run_if(|config: Res| config.render_aabbs), + .add_systems( + PostUpdate, + debug_render_aabbs + .run_if(|config: Res| config.render_aabbs) + .after(PhysicsSet::Sync), ) - .add_system( + .add_systems( + PostUpdate, debug_render_contacts - .run_if(|config: Res| config.render_contacts), + .run_if(|config: Res| config.render_contacts) + .after(PhysicsSet::Sync), ); } } @@ -45,48 +52,50 @@ impl Default for PhysicsDebugConfig { } } -fn debug_render_aabbs(aabbs: Query<&ColliderAabb>, mut shapes: ResMut) { +fn debug_render_aabbs(aabbs: Query<&ColliderAabb>, mut gizmos: Gizmos) { #[cfg(feature = "2d")] for aabb in aabbs.iter() { - shapes.rect().min_max( - Vector::from(aabb.mins).as_f32(), - Vector::from(aabb.maxs).as_f32(), + gizmos.cuboid( + Transform::from_scale(Vector::from(aabb.extents()).extend(0.0).as_f32()) + .with_translation(Vector::from(aabb.center()).extend(0.0).as_f32()), + Color::WHITE, ); } #[cfg(feature = "3d")] for aabb in aabbs.iter() { - shapes.cuboid().min_max( - Vector::from(aabb.mins).as_f32(), - Vector::from(aabb.maxs).as_f32(), + gizmos.cuboid( + Transform::from_scale(Vector::from(aabb.extents()).as_f32()) + .with_translation(Vector::from(aabb.center()).as_f32()), + Color::WHITE, ); } } #[allow(clippy::unnecessary_cast)] -fn debug_render_contacts(mut collisions: EventReader, mut lines: ResMut) { +fn debug_render_contacts(mut collisions: EventReader, mut gizmos: Gizmos) { #[cfg(feature = "2d")] for Collision(contact) in collisions.iter() { - let p1 = contact.point1.extend(0.0).as_f32(); - let p2 = contact.point2.extend(0.0).as_f32(); + let p1 = contact.point1.as_f32(); + let p2 = contact.point2.as_f32(); - lines.line_colored(p1 - Vec3::X * 0.3, p1 + Vec3::X * 0.3, 0.01, Color::CYAN); - lines.line_colored(p1 - Vec3::Y * 0.3, p1 + Vec3::Y * 0.3, 0.01, Color::CYAN); + gizmos.line_2d(p1 - Vec2::X * 0.3, p1 + Vec2::X * 0.3, Color::CYAN); + gizmos.line_2d(p1 - Vec2::Y * 0.3, p1 + Vec2::Y * 0.3, Color::CYAN); - lines.line_colored(p2 - Vec3::X * 0.3, p2 + Vec3::X * 0.3, 0.01, Color::CYAN); - lines.line_colored(p2 - Vec3::Y * 0.3, p2 + Vec3::Y * 0.3, 0.01, Color::CYAN); + gizmos.line_2d(p2 - Vec2::X * 0.3, p2 + Vec2::X * 0.3, Color::CYAN); + gizmos.line_2d(p2 - Vec2::Y * 0.3, p2 + Vec2::Y * 0.3, Color::CYAN); } #[cfg(feature = "3d")] for Collision(contact) in collisions.iter() { let p1 = contact.point1.as_f32(); let p2 = contact.point2.as_f32(); - lines.line_colored(p1 - Vec3::X * 0.3, p1 + Vec3::X * 0.3, 0.01, Color::CYAN); - lines.line_colored(p1 - Vec3::Y * 0.3, p1 + Vec3::Y * 0.3, 0.01, Color::CYAN); - lines.line_colored(p1 - Vec3::Z * 0.3, p1 + Vec3::Z * 0.3, 0.01, Color::CYAN); + gizmos.line(p1 - Vec3::X * 0.3, p1 + Vec3::X * 0.3, Color::CYAN); + gizmos.line(p1 - Vec3::Y * 0.3, p1 + Vec3::Y * 0.3, Color::CYAN); + gizmos.line(p1 - Vec3::Z * 0.3, p1 + Vec3::Z * 0.3, Color::CYAN); - lines.line_colored(p2 - Vec3::X * 0.3, p2 + Vec3::X * 0.3, 0.01, Color::CYAN); - lines.line_colored(p2 - Vec3::Y * 0.3, p2 + Vec3::Y * 0.3, 0.01, Color::CYAN); - lines.line_colored(p2 - Vec3::Z * 0.3, p2 + Vec3::Z * 0.3, 0.01, Color::CYAN); + gizmos.line(p2 - Vec3::X * 0.3, p2 + Vec3::X * 0.3, Color::CYAN); + gizmos.line(p2 - Vec3::Y * 0.3, p2 + Vec3::Y * 0.3, Color::CYAN); + gizmos.line(p2 - Vec3::Z * 0.3, p2 + Vec3::Z * 0.3, Color::CYAN); } } diff --git a/src/plugins/integrator.rs b/src/plugins/integrator.rs index f9d34ee6..b0fd5e02 100644 --- a/src/plugins/integrator.rs +++ b/src/plugins/integrator.rs @@ -22,7 +22,7 @@ impl Plugin for IntegratorPlugin { .add_systems((integrate_pos, integrate_rot).in_set(SubstepSet::Integrate)); app.get_schedule_mut(PhysicsSchedule) .expect("add PhysicsSchedule first") - .add_system( + .add_systems( clear_external_force_and_torque .after(PhysicsSet::Substeps) .before(PhysicsSet::Sleeping), diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 69b868c9..e863e80d 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -67,6 +67,25 @@ use bevy::prelude::*; /// /// You can also find more information regarding the engine's general plugin architecture [here](plugins). /// +/// ## Custom schedule +/// +/// You can run the [`PhysicsSchedule`] in any schedule you want. +/// You can set the schedule when adding the plugin group: +/// +/// ```no_run +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn main() { +/// App::new() +/// .add_plugins((DefaultPlugins, PhysicsPlugins::new(FixedUpdate))) +/// .run(); +/// } +/// ``` +/// /// ## Custom plugins /// /// First, create a new plugin. If you want to run your systems in the engine's schedules, get either the [`PhysicsSchedule`] @@ -92,7 +111,7 @@ use bevy::prelude::*; /// .expect("add PhysicsSchedule first"); /// /// // Add the system into the broad phase system set -/// physics_schedule.add_system(collect_collision_pairs.in_set(PhysicsSet::BroadPhase)); +/// physics_schedule.add_systems(collect_collision_pairs.in_set(PhysicsSet::BroadPhase)); /// } /// } /// @@ -122,7 +141,7 @@ use bevy::prelude::*; /// /// // Add PhysicsPlugins and replace default broad phase with our custom broad phase /// app.add_plugins( -/// PhysicsPlugins +/// PhysicsPlugins::default() /// .build() /// .disable::() /// .add(CustomBroadPhasePlugin), @@ -134,7 +153,26 @@ use bevy::prelude::*; /// /// You can find a full working example /// [here](https://github.com/Jondolf/bevy_xpbd/blob/main/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs). -pub struct PhysicsPlugins; +pub struct PhysicsPlugins { + schedule: Box, +} + +impl PhysicsPlugins { + /// Creates a [`PhysicsPlugins`] plugin group using the given schedule for running the [`PhysicsSchedule`]. + /// + /// The default schedule is `PostUpdate`. + pub fn new(schedule: S) -> Self { + Self { + schedule: Box::new(schedule), + } + } +} + +impl Default for PhysicsPlugins { + fn default() -> Self { + Self::new(PostUpdate) + } +} impl PluginGroup for PhysicsPlugins { fn build(self) -> PluginGroupBuilder { @@ -147,7 +185,7 @@ impl PluginGroup for PhysicsPlugins { } builder - .add(PhysicsSetupPlugin) + .add(PhysicsSetupPlugin::new(self.schedule)) .add(PreparePlugin) .add(BroadPhasePlugin) .add(IntegratorPlugin) diff --git a/src/plugins/prepare.rs b/src/plugins/prepare.rs index fdfbdc43..d0219be1 100644 --- a/src/plugins/prepare.rs +++ b/src/plugins/prepare.rs @@ -19,8 +19,9 @@ pub struct PreparePlugin; impl Plugin for PreparePlugin { fn build(&self, app: &mut bevy::prelude::App) { - app.configure_set(ComponentInitSet.in_set(PhysicsSet::Prepare)); + app.configure_set(Update, ComponentInitSet.in_set(PhysicsSet::Prepare)); app.add_systems( + Update, (init_rigid_bodies, init_mass_properties, init_colliders).in_set(ComponentInitSet), ); diff --git a/src/plugins/setup.rs b/src/plugins/setup.rs index 6e73cbab..4774cb11 100644 --- a/src/plugins/setup.rs +++ b/src/plugins/setup.rs @@ -2,6 +2,8 @@ //! //! See [`PhysicsSetupPlugin`]. +use bevy::transform::TransformSystem; + use crate::prelude::*; /// Sets up the physics engine by initializing the necessary schedules, sets and resources. @@ -25,7 +27,26 @@ use crate::prelude::*; /// and it typically handles things like collision detection and constraint solving. /// /// [Substepping sets](SubstepSet) are added by the solver plugin if it is enabled. See [`SolverPlugin`] for more information. -pub struct PhysicsSetupPlugin; +pub struct PhysicsSetupPlugin { + schedule: Box, +} + +impl PhysicsSetupPlugin { + /// Creates a [`PhysicsSetupPlugin`] using the given schedule for running the [`PhysicsSchedule`]. + /// + /// The default schedule is `PostUpdate`. + pub fn new(schedule: S) -> Self { + Self { + schedule: Box::new(schedule), + } + } +} + +impl Default for PhysicsSetupPlugin { + fn default() -> Self { + Self::new(PostUpdate) + } +} impl Plugin for PhysicsSetupPlugin { fn build(&self, app: &mut App) { @@ -109,17 +130,19 @@ impl Plugin for PhysicsSetupPlugin { app.add_schedule(SubstepSchedule, substep_schedule); // Add system set for running physics schedule + let schedule = &self.schedule; app.configure_set( - FixedUpdateSet - .before(CoreSet::Update) - .in_base_set(CoreSet::PreUpdate), + schedule.dyn_clone(), + FixedUpdateSet.before(TransformSystem::TransformPropagate), + ); + app.add_systems( + schedule.dyn_clone(), + run_physics_schedule.in_set(FixedUpdateSet), ); - app.add_system(run_physics_schedule.in_set(FixedUpdateSet)); - app.add_system( - run_substep_schedule - .in_set(PhysicsSet::Substeps) - .in_schedule(PhysicsSchedule), + app.add_systems( + PhysicsSchedule, + run_substep_schedule.in_set(PhysicsSet::Substeps), ); } } diff --git a/src/plugins/solver.rs b/src/plugins/solver.rs index 1a8d8969..16adf5fb 100644 --- a/src/plugins/solver.rs +++ b/src/plugins/solver.rs @@ -216,7 +216,7 @@ fn penetration_constraints( /// .get_schedule_mut(SubstepSchedule) /// .expect("add SubstepSchedule first"); /// -/// substeps.add_system( +/// substeps.add_systems( /// solve_constraint:: /// .in_set(SubstepSet::SolveUserConstraints), /// ); diff --git a/src/plugins/sync.rs b/src/plugins/sync.rs index e447704e..26f91ac1 100644 --- a/src/plugins/sync.rs +++ b/src/plugins/sync.rs @@ -17,7 +17,7 @@ impl Plugin for SyncPlugin { fn build(&self, app: &mut bevy::prelude::App) { app.get_schedule_mut(PhysicsSchedule) .expect("add PhysicsSchedule first") - .add_system(sync_transforms.in_set(PhysicsSet::Sync)); + .add_systems(sync_transforms.in_set(PhysicsSet::Sync)); } } diff --git a/src/resources.rs b/src/resources.rs index af1fb735..7eb5a6ff 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -19,8 +19,7 @@ use crate::prelude::*; /// /// fn main() { /// App::new() -/// .add_plugins(DefaultPlugins) -/// .add_plugins(PhysicsPlugins) +/// .add_plugins((DefaultPlugins, PhysicsPlugins::default())) /// // Use a 120 Hz fixed timestep instead of the default 60 Hz /// .insert_resource(PhysicsTimestep::Fixed(1.0 / 120.0)) /// .run(); @@ -81,8 +80,7 @@ pub struct SubDeltaTime(pub Scalar); /// /// fn main() { /// App::new() -/// .add_plugins(DefaultPlugins) -/// .add_plugins(PhysicsPlugins) +/// .add_plugins((DefaultPlugins, PhysicsPlugins::default())) /// .insert_resource(SubstepCount(30)) /// .run(); /// } @@ -173,8 +171,7 @@ impl Default for DeactivationTime { /// # #[cfg(all(feature = "3d", feature = "f32"))] /// fn main() { /// App::new() -/// .add_plugins(DefaultPlugins) -/// .add_plugins(PhysicsPlugins) +/// .add_plugins((DefaultPlugins, PhysicsPlugins::default())) /// .insert_resource(Gravity(Vec3::NEG_Y * 19.6)) /// .run(); /// } diff --git a/src/tests.rs b/src/tests.rs index da14f33d..b208f095 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -23,9 +23,11 @@ macro_rules! setup_insta { fn create_app() -> App { let mut app = App::new(); - app.add_plugins(MinimalPlugins); - app.add_plugins(PhysicsPlugins); - app.add_plugin(LogPlugin::default()); + app.add_plugins(( + MinimalPlugins, + PhysicsPlugins::default(), + LogPlugin::default(), + )); app.insert_resource(TimeUpdateStrategy::ManualInstant(Instant::now())); app } @@ -77,7 +79,6 @@ fn setup_cubes_simulation(mut commands: Commands) { #[test] fn it_loads_plugin_without_errors() -> Result<(), Box> { let mut app = create_app(); - app.setup(); for _ in 0..500 { tick_60_fps(&mut app); @@ -92,7 +93,7 @@ fn body_with_velocity_moves() { app.insert_resource(Gravity::ZERO); - app.add_startup_system(|mut commands: Commands| { + app.add_systems(Startup, |mut commands: Commands| { // move right at 1 unit per second commands.spawn(( SpatialBundle::default(), @@ -101,8 +102,6 @@ fn body_with_velocity_moves() { )); }); - app.setup(); - const UPDATES: usize = 500; for _ in 0..UPDATES { @@ -141,9 +140,7 @@ fn cubes_simulation_is_deterministic_across_machines() { setup_insta!(); let mut app = create_app(); - app.add_startup_system(setup_cubes_simulation); - - app.setup(); + app.add_systems(Startup, setup_cubes_simulation); const SECONDS: usize = 10; const UPDATES: usize = 60 * SECONDS; @@ -168,9 +165,7 @@ fn cubes_simulation_is_locally_deterministic() { fn run_cubes() -> Vec<(Id, Transform)> { let mut app = create_app(); - app.add_startup_system(setup_cubes_simulation); - - app.setup(); + app.add_systems(Startup, setup_cubes_simulation); const SECONDS: usize = 5; const UPDATES: usize = 60 * SECONDS;