wde_physics/lib.rs
1//! Provides physics world management, colliders, and raycasting functionalities using the Rapier physics engine.
2//!
3//! # Features
4//! - **Colliders**: Add physics colliders to entities for collision detection (see [`Collider`](crate::colliders::Collider) for supported shapes)
5//! - **Raycasting**: Cast rays through the physics world to detect intersections (see [`PhysicsWorld`](crate::prelude::PhysicsWorld) for raycasting methods)
6//! - **Automatic Updates**: Colliders automatically sync with entity transforms
7//!
8//! # Creating Colliders
9//! Attach a collider component to any entity with a transform:
10//! ```rust
11//! commands.spawn((
12//! Transform::from_xyz(0.0, -1.0, 0.0),
13//! Collider::from(CuboidCollider::new(Vec3::new(10.0, 1.0, 10.0)).with_group(ColliderGroup::GROUP_1)),
14//! ));
15//! ```
16//! This example creates a large flat collider representing the ground, assigned to `GROUP_1`. You can create various shapes (e.g., boxes, spheres, heightfields) and assign them to different groups for selective raycasting.
17//!
18//! # Raycasting
19//! Cast rays to detect physics intersections:
20//! ```rust
21//! fn cast_ray_system(phworld: Res<PhysicsWorld>) {
22//! let ray = Ray::new(
23//! Vec3::new(0.0, 10.0, 0.0), // Origin above the ground
24//! Vec3::new(0.0, -1.0, 0.0), // Direction pointing downwards
25//! );
26//!
27//! // Cast the ray and check for hits
28//! // Use a filter to only hit colliders in GROUP_1
29//! if let Some((entity, toi)) = phworld.cast_ray(&ray, &RayCastConfig::with_filter(ColliderGroup::GROUP_1)) {
30//! let hit_point = ray.point_at(toi);
31//! println!("Hit entity {:?} at {:?}", entity, hit_point);
32//! }
33//! }
34//! ```
35//!
36//! # Camera-Based Raycasting
37//! Cast rays from screen coordinates (useful for mouse picking):
38//! ```rust
39//! fn mouse_picking(
40//! mut commands: Commands,
41//! phworld: Res<PhysicsWorld>,
42//! window: Single<&Window, With<PrimaryWindow>>,
43//! camera_query: Query<(&GlobalTransform, &CameraView), With<Camera>>,
44//! ) {
45//! // Get cursor position in NDC
46//! let Some(cursor_pos) = window.cursor_position() else { return };
47//! let cursor_ndc = cursor_pos / window.size();
48//!
49//! // Get camera data
50//! let Ok((camera_transform, camera_view)) = camera_query.get_single() else { return };
51//! let aspect_ratio = window.size().x / window.size().y;
52//!
53//! // Create ray from camera
54//! let ray = Ray::from_ndc(cursor_ndc, aspect_ratio, camera_transform, camera_view);
55//!
56//! // Cast the ray
57//! if let Some((entity, toi)) = phworld.cast_ray(&ray, &RayCastConfig::default()) {
58//! let hit_point = ray.point_at(toi);
59//! println!("Clicked on entity {:?} at {:?}", entity, hit_point);
60//! }
61//! }
62//! ```
63use bevy::prelude::*;
64
65use crate::core::{PhysicsWorld, handle_changes};
66
67#[doc(hidden)]
68pub mod prelude {
69 pub use crate::colliders::{BoxCollider, Collider, ColliderGroup, HeightfieldCollider};
70 pub use crate::core::PhysicsWorld;
71 pub use crate::raycasting::{Ray, RayCastConfig};
72}
73
74mod colliders;
75mod core;
76mod raycasting;
77
78pub struct PhysicsPlugin;
79impl Plugin for PhysicsPlugin {
80 fn build(&self, app: &mut App) {
81 app.init_resource::<PhysicsWorld>()
82 .add_systems(Update, handle_changes);
83 }
84}