Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
902 changes: 902 additions & 0 deletions Claude.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/rapier2d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ features = ["parallel", "simd-stable", "serde-serialize", "debug-render"]
name = "rapier2d_f64"
path = "../../src/lib.rs"
required-features = ["dim2", "f64"]

doctest = false # All doctests are written assuming the 3D version.

[dependencies]
vec_map = { version = "0.8", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/rapier2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ features = ["parallel", "simd-stable", "serde-serialize", "debug-render"]
name = "rapier2d"
path = "../../src/lib.rs"
required-features = ["dim2", "f32"]

doctest = false # All doctests are written assuming the 3D version.

[dependencies]
vec_map = { version = "0.8", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/rapier3d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ features = ["parallel", "simd-stable", "serde-serialize", "debug-render"]
name = "rapier3d_f64"
path = "../../src/lib.rs"
required-features = ["dim3", "f64"]

doctest = false # All doctests are written assuming the 3D f32 version.

[dependencies]
vec_map = { version = "0.8", optional = true }
Expand Down
56 changes: 55 additions & 1 deletion src/control/character_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,61 @@ pub struct CharacterCollision {
pub hit: ShapeCastHit,
}

/// A character controller for kinematic bodies.
/// A kinematic character controller for player/NPC movement (walking, climbing, sliding).
///
/// This provides classic game character movement: walking on floors, sliding on slopes,
/// climbing stairs, and snapping to ground. It's kinematic (not physics-based), meaning
/// you control movement directly rather than applying forces.
///
/// **Not suitable for:** Ragdolls, vehicles, or physics-driven movement (use dynamic bodies instead).
///
/// # How it works
///
/// 1. You provide desired movement (e.g., "move forward 5 units")
/// 2. Controller casts the character shape through the world
/// 3. It handles collisions: sliding along walls, stepping up stairs, snapping to ground
/// 4. Returns the final movement to apply
///
/// # Example
///
/// ```
/// # use rapier3d::prelude::*;
/// # use rapier3d::control::{CharacterAutostep, KinematicCharacterController};
/// # use nalgebra::Isometry3;
/// # let mut bodies = RigidBodySet::new();
/// # let mut colliders = ColliderSet::new();
/// # let broad_phase = BroadPhaseBvh::new();
/// # let narrow_phase = NarrowPhase::new();
/// # let dt = 1.0 / 60.0;
/// # let speed = 5.0;
/// # let (input_x, input_z) = (1.0, 0.0);
/// # let character_shape = Ball::new(0.5);
/// # let mut character_pos = Isometry3::identity();
/// # let query_pipeline = broad_phase.as_query_pipeline(
/// # narrow_phase.query_dispatcher(),
/// # &bodies,
/// # &colliders,
/// # QueryFilter::default(),
/// # );
/// let controller = KinematicCharacterController {
/// slide: true, // Slide along walls instead of stopping
/// autostep: Some(CharacterAutostep::default()), // Auto-climb stairs
/// max_slope_climb_angle: 45.0_f32.to_radians(), // Max climbable slope
/// ..Default::default()
/// };
///
/// // In your game loop:
/// let desired_movement = vector![input_x, 0.0, input_z] * speed * dt;
/// let movement = controller.move_shape(
/// dt,
/// &query_pipeline,
/// &character_shape,
/// &character_pos,
/// desired_movement,
/// |_| {} // Collision event callback
/// );
/// character_pos.translation.vector += movement.translation;
/// ```
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug)]
pub struct KinematicCharacterController {
Expand Down
125 changes: 50 additions & 75 deletions src/data/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ enum Entry<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx = arena.insert(123);
/// assert_eq!(arena[idx], 123);
Expand Down Expand Up @@ -94,9 +93,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::<usize>::new();
/// # let _ = arena;
/// ```
Expand All @@ -110,9 +108,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::with_capacity(10);
///
/// // These insertions will not require further allocation.
Expand All @@ -139,9 +136,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::with_capacity(1);
/// arena.insert(42);
/// arena.insert(43);
Expand Down Expand Up @@ -177,9 +173,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
///
/// match arena.try_insert(42) {
Expand Down Expand Up @@ -218,9 +213,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::{Arena, Index};
///
/// ```
/// # use rapier3d::data::arena::{Arena, Index};
/// let mut arena = Arena::new();
///
/// match arena.try_insert_with(|idx| (42, idx)) {
Expand Down Expand Up @@ -272,9 +266,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
///
/// let idx = arena.insert(42);
Expand All @@ -295,9 +288,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::{Arena, Index};
///
/// ```
/// # use rapier3d::data::arena::{Arena, Index};
/// let mut arena = Arena::new();
///
/// let idx = arena.insert_with(|idx| (42, idx));
Expand Down Expand Up @@ -337,9 +329,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx = arena.insert(42);
///
Expand Down Expand Up @@ -381,9 +372,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut crew = Arena::new();
/// crew.extend(&["Jim Hawkins", "John Silver", "Alexander Smollett", "Israel Hands"]);
/// let pirates = ["John Silver", "Israel Hands"]; // too dangerous to keep them around
Expand Down Expand Up @@ -422,9 +412,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx = arena.insert(42);
///
Expand All @@ -443,9 +432,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx = arena.insert(42);
///
Expand All @@ -469,9 +457,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx = arena.insert(42);
///
Expand Down Expand Up @@ -500,9 +487,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx1 = arena.insert(0);
/// let idx2 = arena.insert(1);
Expand Down Expand Up @@ -565,9 +551,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// assert_eq!(arena.len(), 0);
///
Expand All @@ -588,9 +573,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// assert!(arena.is_empty());
///
Expand All @@ -612,9 +596,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::with_capacity(10);
/// assert_eq!(arena.capacity(), 10);
///
Expand All @@ -640,9 +623,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::with_capacity(10);
/// arena.reserve(5);
/// assert_eq!(arena.capacity(), 15);
Expand Down Expand Up @@ -675,9 +657,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// for i in 0..10 {
/// arena.insert(i * i);
Expand All @@ -702,9 +683,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// for i in 0..10 {
/// arena.insert(i * i);
Expand All @@ -731,9 +711,8 @@ impl<T> Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx_1 = arena.insert("hello");
/// let idx_2 = arena.insert("world");
Expand Down Expand Up @@ -818,9 +797,8 @@ impl<T> IntoIterator for Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// for i in 0..10 {
/// arena.insert(i * i);
Expand Down Expand Up @@ -902,9 +880,8 @@ impl<'a, T> IntoIterator for &'a Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// for i in 0..10 {
/// arena.insert(i * i);
Expand Down Expand Up @@ -1006,9 +983,8 @@ impl<'a, T> IntoIterator for &'a mut Arena<T> {
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// for i in 0..10 {
/// arena.insert(i * i);
Expand Down Expand Up @@ -1104,9 +1080,8 @@ impl<T> FusedIterator for IterMut<'_, T> {}
///
/// # Examples
///
/// ```ignore
/// use rapier::data::arena::Arena;
///
/// ```
/// # use rapier3d::data::arena::Arena;
/// let mut arena = Arena::new();
/// let idx_1 = arena.insert("hello");
/// let idx_2 = arena.insert("world");
Expand Down
Loading