Import Oxide engine (Stages 0–10) under MIT license

Full project snapshot migrated to new Gitea remote without history:
engine, editor, physics, script, examples, tests, docs, and assets.
Relicensed from GPLv3 to MIT and updated repo URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Homer Simpson
2026-07-05 20:41:02 +02:00
parent 2afb56b329
commit 9eead719b0
157 changed files with 47270 additions and 2 deletions
+101
View File
@@ -0,0 +1,101 @@
//! The [`CharacterController`] component — a kinematic capsule character.
//!
//! A character controller is *not* a rigid body: it never reacts to forces.
//! Instead the game asks it to move by some desired translation each frame and
//! the controller resolves that against the world's colliders — sliding along
//! walls, stepping up small ledges, sticking to the ground on slopes, and
//! refusing to climb anything too steep — then reports whether the character
//! ended up grounded. It is the basis for player/NPC movement and is reused by
//! the Stage-15 prototyping kit.
//!
//! The character is a **capsule** (radius + cylindrical half-height along `+Y`).
//! An entity with this component is moved through
//! [`PhysicsWorld::move_character`](crate::PhysicsWorld::move_character); it does
//! **not** carry a [`RigidBody`](crate::RigidBody)/[`Collider`](crate::Collider)
//! (it isn't simulated), so it never appears in the body set and never
//! self-collides.
use oxide_engine::math::Vec3;
use oxide_engine::reflect::Reflect;
use serde::{Deserialize, Serialize};
/// Component: a kinematic capsule character moved by move-and-slide.
///
/// Tune the capsule shape (`radius`/`half_height`) and the movement rules
/// (`max_slope_degrees`, `step_offset`, `snap_to_ground`, `skin_width`). Pass an
/// entity carrying this to
/// [`PhysicsWorld::move_character`](crate::PhysicsWorld::move_character).
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
pub struct CharacterController {
/// Capsule radius.
pub radius: f32,
/// Capsule cylindrical half-height (the straight segment between the two
/// hemispherical caps), along local `+Y`.
pub half_height: f32,
/// Steepest ground slope (degrees from horizontal) the character will walk
/// up; steeper surfaces are treated as walls.
pub max_slope_degrees: f32,
/// Tallest step/ledge the character will automatically climb. `0` disables
/// auto-stepping.
pub step_offset: f32,
/// Distance below the feet within which the character snaps down to stay
/// grounded (prevents bouncing down stairs/slopes). `0` disables snapping.
pub snap_to_ground: f32,
/// A small collision-detection margin kept around the capsule.
pub skin_width: f32,
}
impl Default for CharacterController {
fn default() -> Self {
Self {
radius: 0.3,
half_height: 0.6,
max_slope_degrees: 45.0,
step_offset: 0.3,
snap_to_ground: 0.2,
skin_width: 0.01,
}
}
}
impl CharacterController {
/// The total half-height of the capsule (cylinder half-height + one radius),
/// i.e. the distance from the capsule center to either tip.
pub fn total_half_height(&self) -> f32 {
self.half_height + self.radius
}
/// The local-space offset from the capsule center down to the feet (the
/// bottom tip), useful for placing/grounding the character.
pub fn foot_offset(&self) -> Vec3 {
Vec3::new(0.0, -self.total_half_height(), 0.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_a_reasonable_humanoid_capsule() {
let c = CharacterController::default();
assert_eq!(c.radius, 0.3);
assert!((c.total_half_height() - 0.9).abs() < 1e-6);
assert!((c.foot_offset() - Vec3::new(0.0, -0.9, 0.0)).length() < 1e-6);
}
#[test]
fn round_trips_through_ron() {
let c = CharacterController {
radius: 0.4,
half_height: 1.0,
max_slope_degrees: 50.0,
step_offset: 0.0,
snap_to_ground: 0.0,
skin_width: 0.02,
};
let ron = ron::to_string(&c).unwrap();
let back: CharacterController = ron::from_str(&ron).unwrap();
assert_eq!(c, back);
}
}