diff --git a/Cargo.lock b/Cargo.lock index f2eb0f4..43073ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "alsa" version = "0.9.1" @@ -967,6 +973,19 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7370d0e46b60e071917711d0860721f5347bc958bf325975ae6913a5dfcf01" +[[package]] +name = "bevy_rapier3d" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf74109573c2c82b05b217cb6101f7e71e6c53ad622aed6c370cc5783c59eb8" +dependencies = [ + "bevy", + "bitflags 2.9.1", + "log", + "nalgebra", + "rapier3d", +] + [[package]] name = "bevy_reflect" version = "0.16.1" @@ -1782,6 +1801,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -1791,6 +1823,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -1915,6 +1966,15 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + [[package]] name = "encase" version = "0.10.0" @@ -2144,6 +2204,7 @@ name = "games" version = "0.1.0" dependencies = [ "bevy", + "bevy_rapier3d", ] [[package]] @@ -2393,6 +2454,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ + "allocator-api2", "equivalent", "foldhash", "serde", @@ -2719,6 +2781,16 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.7.5" @@ -2808,6 +2880,34 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "nalgebra" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" +dependencies = [ + "approx", + "glam", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ndk" version = "0.8.0" @@ -2920,6 +3020,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-derive" version = "0.4.2" @@ -2931,6 +3050,26 @@ dependencies = [ "syn", ] +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3250,6 +3389,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -3294,6 +3442,31 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parry3d" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec55ce6f725367f8149f750575e79a8879d71b7257c02273259f9375822821f" +dependencies = [ + "approx", + "arrayvec", + "bitflags 2.9.1", + "downcast-rs 2.0.1", + "either", + "ena", + "hashbrown", + "log", + "nalgebra", + "num-derive", + "num-traits", + "ordered-float 5.0.0", + "rstar", + "simba", + "slab", + "spade", + "thiserror 2.0.12", +] + [[package]] name = "paste" version = "1.0.15" @@ -3461,6 +3634,19 @@ name = "profiling" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn", +] [[package]] name = "quick-xml" @@ -3544,12 +3730,42 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +[[package]] +name = "rapier3d" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35ec3d01c4f918675411442024a1fbfb7eafdd878a6e82479ff6e461a9092fc" +dependencies = [ + "approx", + "arrayvec", + "bit-vec 0.8.0", + "bitflags 2.9.1", + "crossbeam", + "downcast-rs 2.0.1", + "log", + "nalgebra", + "num-derive", + "num-traits", + "ordered-float 5.0.0", + "parry3d", + "profiling", + "rustc-hash 2.1.1", + "simba", + "thiserror 2.0.12", +] + [[package]] name = "raw-window-handle" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "read-fonts" version = "0.29.3" @@ -3634,6 +3850,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "robust" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e27ee8bb91ca0adcf0ecb116293afa12d393f9c2b9b9cd54d33e8078fe19839" + [[package]] name = "rodio" version = "0.20.1" @@ -3662,6 +3884,17 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "rstar" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" +dependencies = [ + "heapless", + "num-traits", + "smallvec", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -3738,6 +3971,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3831,6 +4073,19 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -3902,6 +4157,18 @@ dependencies = [ "serde", ] +[[package]] +name = "spade" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14e31a007e9f85c32784b04f89e6e194bb252a4d41b4a8ccd9e77245d901c8c" +dependencies = [ + "hashbrown", + "num-traits", + "robust", + "smallvec", +] + [[package]] name = "spin" version = "0.9.8" @@ -4260,6 +4527,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -4666,7 +4939,7 @@ dependencies = [ "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", - "ordered-float", + "ordered-float 4.6.0", "parking_lot", "profiling", "range-alloc", @@ -4695,6 +4968,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 6a5a6d9..4935160 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ name = "games" version = "0.1.0" edition = "2024" +[dependencies.bevy_rapier3d] +version = "0.30.0" + [dependencies.bevy] version = "0.16.1" features = ["wayland", "dynamic_linking"] diff --git a/examples/sensors.rs b/examples/sensors.rs new file mode 100644 index 0000000..d31356c --- /dev/null +++ b/examples/sensors.rs @@ -0,0 +1,138 @@ +use bevy::{color::palettes::css::{BLUE, GREEN}, render::mesh::{SphereKind, SphereMeshBuilder}}; +use bevy_rapier3d::rapier::prelude::CollisionEventFlags; +use games::*; + +fn main() { + App::new() + .add_plugins(BaseGamePlugin) + .add_systems(Startup, ( + setup_physics_scene, + position_camera.after(setup_camera) + ).chain()) + .add_systems(Update, (control_ball, read_events)) + .run(); +} + +fn setup_physics_scene( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut> +) { + // Create the ground. + // Make this a physical looking object + commands + .spawn(( + Collider::cuboid(100.0, 0.1, 100.0), + Transform::from_xyz(0.0, -2.0, 0.0), + Mesh3d(meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(50.0)))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: RED.into(), + ..Default::default() + })), + )); + + commands.spawn(( + SpotLight { color: WHITE.into(), intensity: 10_000_000.0, shadows_enabled: true, ..default() }, + Transform::from_xyz(0.0, 10.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y), + )); + + // Create the bouncing ball. + commands + .spawn(( + RigidBody::Dynamic, + GravityScale(1.0), + Collider::ball(0.5), + ColliderMassProperties::Mass(2.0), + Restitution::coefficient(0.7), + ExternalImpulse::default(), + Transform::from_xyz(0.0, 4.0, 0.0), + Marker, + ActiveEvents::all(), + Mesh3d(meshes.add(SphereMeshBuilder::new( + 0.5, + SphereKind::Uv { + sectors: 10, + stacks: 10, + }, + ))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: BLUE.into(), + ..Default::default() + })), + )); + + // Create a box that the ball can pass into/through + // TODO: Transparent box to visualize area + commands + .spawn(( + Sensor, + Marker, + Collider::cuboid(1.0, 1.0, 1.0), + Transform::from_xyz(0.0, 0.0, 0.0), + Mesh3d(meshes.add(Cuboid::new(2.0, 2.0, 2.0))), + MeshMaterial3d(materials.add(StandardMaterial { + base_color: GREEN.with_alpha(0.5).into(), + alpha_mode: AlphaMode::Blend, + ..Default::default() + })), + )); +} + +/// Position the world camera to get a better view +fn position_camera( + mut query: Query<(Entity, &mut Transform), (With, With)>, + mut commands: Commands, +) { + query.iter_mut().for_each(|(e, mut t)| { + *t = Transform::from_xyz(10.0, 10.0, 10.0) + .looking_at(Vec3::ZERO, Vec3::Y); + }) +} + +fn control_ball( + keys: Res>, + mut ball: Single<&mut ExternalImpulse>, +) { + if keys.pressed(KeyCode::ArrowUp) { + ball.torque_impulse = Vec3::new(0.0, 0.0, 0.1); + } + + if keys.pressed(KeyCode::ArrowDown) { + ball.torque_impulse = Vec3::new(0.0, 0.0, -0.1); + } + + if keys.pressed(KeyCode::ArrowLeft) { + ball.torque_impulse = Vec3::new(0.1, 0.0, 0.0); + } + + if keys.pressed(KeyCode::ArrowRight) { + ball.torque_impulse = Vec3::new(-0.1, 0.0, 0.0); + } +} + +#[derive(Debug)] +enum WithinBounds { + Enter(Entity, Entity), + Exit(Entity, Entity), +} + +#[derive(Component, Debug)] +struct Marker; + +fn read_events( + mut events: EventReader, + mut commands: Commands, +) { + events.read().filter_map(|collision_event| { + match collision_event { + CollisionEvent::Started(a, b, flags) => { + (*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Enter(*a, *b)) + }, + CollisionEvent::Stopped(a, b, flags) => { + (*flags == CollisionEventFlags::SENSOR).then_some(WithinBounds::Exit(*a, *b)) + } + } + }).for_each(|within_bounds| { + info!("Event: {:?}", within_bounds); + }) +} diff --git a/examples/sync_resource_to_ui.rs b/examples/sync_resource_to_ui.rs index 263e019..4738f18 100644 --- a/examples/sync_resource_to_ui.rs +++ b/examples/sync_resource_to_ui.rs @@ -1,12 +1,9 @@ -use bevy::input::mouse::MouseMotion; use games::*; fn main() { App::new() .init_resource::() - .add_plugins(BaseGamePlugin { - camera: CameraType::Camera3d, - }) + .add_plugins(BaseGamePlugin) .add_systems(Startup, init_ui) .add_systems( Update, diff --git a/src/base_game.rs b/src/base_game.rs index 34958d7..7f376f4 100644 --- a/src/base_game.rs +++ b/src/base_game.rs @@ -1,31 +1,17 @@ use super::*; /// A good starting place for creating a game building on top of the base Bevy app -pub struct BaseGamePlugin { - pub camera: CameraType, -} +pub struct BaseGamePlugin; impl Plugin for BaseGamePlugin { fn build(&self, app: &mut App) { - app.add_plugins(DefaultPlugins).add_plugins(DebuggingPlugin); - - // Add a camera setup system and startup - app.add_systems( - Startup, - match self.camera { - CameraType::Camera2d => setup_camera_2d, - CameraType::Camera3d => setup_camera_3d, - }, - ); + app.add_plugins(DefaultPlugins) + .add_plugins(DebuggingPlugin) + .add_plugins(RapierPhysicsPlugin::::default()) + .add_systems(Startup, setup_camera); } } -/// For selecting the type of camera this game requires -pub enum CameraType { - Camera2d, - Camera3d, -} - /// System to toggle the visibility of entities based on their state pub fn toggle_state_visibility( mut q: Query<(Entity, &mut Visibility, &S)>, @@ -41,10 +27,6 @@ pub fn toggle_state_visibility( }); } -fn setup_camera_2d(mut commands: Commands) { - commands.spawn((Camera { ..default() }, Camera2d)); -} - -fn setup_camera_3d(mut commands: Commands) { - commands.spawn((Camera { ..default() }, Camera3d { ..default() })); +pub fn setup_camera(mut commands: Commands) { + commands.spawn((Camera3d { ..default() }, Camera { ..default() })); } diff --git a/src/debug.rs b/src/debug.rs index f813367..27424fc 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -6,11 +6,15 @@ pub struct DebuggingPlugin; impl Plugin for DebuggingPlugin { fn build(&self, app: &mut App) { app.init_state::() + .add_plugins(RapierDebugRenderPlugin::default().disabled()) .add_systems(Startup, init_debug_ui) .add_systems( Update, ( - toggle_state_visibility:: + ( + toggle_state_visibility::, + toggle_rapier_debug_render, + ) .run_if(state_changed::), toggle_debug_state.run_if(on_keyboard_press(KeyCode::F12)), ), @@ -57,3 +61,11 @@ fn init_debug_ui(mut commands: Commands) { }, )); } + +/// Simple system that enables/disables rapier debug visuals when the debugging state changes +fn toggle_rapier_debug_render( + state: Res>, + mut context: ResMut, +) { + context.enabled = *state.get() == DebuggingState::On; +} diff --git a/src/lib.rs b/src/lib.rs index 1242e5a..31e831c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(ambiguous_glob_reexports)] + mod base_game; mod debug; mod scheduling; @@ -7,9 +9,10 @@ pub use std::fmt::Display; pub use bevy::{ color::palettes::css::{BLACK, RED, WHITE}, - input::{ButtonState, keyboard::KeyboardInput}, + input::{ButtonState, keyboard::KeyboardInput, mouse::MouseMotion}, prelude::*, }; +pub use bevy_rapier3d::prelude::*; pub use base_game::*; pub use debug::*;