4.1 KiB
OpenSpec Change: enterprise-gaming-character-physics
- Change ID: enterprise-gaming-character-physics
- Package:
@suaveplan/character-physics - Category: gaming/rigs
- Target path:
gaming/rigs/character-physics - Status: PROPOSED
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/character-physics.md
Summary
Deliver @suaveplan/character-physics on the genesis branch from scratch at enterprise quality. The package provides engine-agnostic character controller physics using a floating capsule model: the character body levitates above the ground by a configurable spring distance, enabling smooth traversal of steps and slopes without physics tunnelling. Plugin-based architecture allows swapping physics backends (Rapier, Cannon, custom). Genesis adds structured errors, Zod option schemas, OTel frame-time histogram, injectable clock and RNG, and 100% coverage including property tests for spring convergence.
Current-state audit (genesis)
Package does not exist on genesis yet — fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
CharacterPhysicsError; CHAR_PHYSICS.* codes |
@suaveplan/types (workspace:^) |
Clock |
@suaveplan/events (workspace:^) |
Ground contact, jump, land events |
zod (catalog:forms) |
CharacterControllerOptions schema |
@opentelemetry/api (catalog:observability) |
Step duration histogram |
Public API surface
function createCharacterController(options: CharacterControllerOptions): CharacterController
interface CharacterControllerOptions {
height: number
radius: number
mass?: number
springStrength?: number
springDamping?: number
maxSlopeAngle?: number // degrees, default 45
stepHeight?: number // max step height, default 0.3
gravityScale?: number
backend?: PhysicsBackend
clock?: Clock
}
interface CharacterController {
move(velocity: Vec3): void
jump(impulse: number): void
teleport(position: Vec3): void
update(dt: number): void
readonly position: Vec3
readonly velocity: Vec3
readonly isGrounded: boolean
readonly groundNormal: Vec3 | null
readonly isJumping: boolean
setGravityScale(scale: number): void
applyImpulse(impulse: Vec3): void
}
interface Vec3 { x: number; y: number; z: number }
// Plugin interface for physics backends
interface PhysicsBackend {
castRay(origin: Vec3, dir: Vec3, maxDist: number): RaycastHit | null
sweep(shape: CapsuleShape, from: Vec3, to: Vec3): SweepHit | null
applyForce(body: BodyHandle, force: Vec3): void
}
Error codes
type CharPhysicsErrorCode =
| 'CHAR_PHYSICS_INVALID_HEIGHT'
| 'CHAR_PHYSICS_BACKEND_NOT_SET'
| 'CHAR_PHYSICS_UPDATE_FAILED'
New additions for genesis
- Spring levitation — PD controller computes spring force each frame to maintain float distance; stable at any
dt. - Slope handling — projects velocity onto ground normal; blocks movement up slopes exceeding
maxSlopeAngle. - Step detection — forward ray + step-up ray distinguish steps from walls for smooth stair climbing.
- Backend abstraction —
PhysicsBackendinterface decouples math from physics engine;NullBackendfor unit tests.
File layout
gaming/rigs/character-physics/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/index.ts
types/common.ts
types/common.md
core/
character-controller/
character-controller.ts
character-controller.md
character-controller.types.ts
character-controller.test.ts
index.ts
backends/
null-backend/
null-backend.ts
null-backend.md
null-backend.test.ts
index.ts
utils/
spring.ts
spring.md
spring.test.ts
slope.ts
slope.md
slope.test.ts
tests/
property.test.ts
Impact
Downstream: @suaveplan/r3f-character wires this controller to Rapier via the backend interface. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events.