Initial Commit
This commit is contained in:
140
openspec/changes/enterprise-gaming-character-physics/proposal.md
Normal file
140
openspec/changes/enterprise-gaming-character-physics/proposal.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# 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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
type CharPhysicsErrorCode =
|
||||
| 'CHAR_PHYSICS_INVALID_HEIGHT'
|
||||
| 'CHAR_PHYSICS_BACKEND_NOT_SET'
|
||||
| 'CHAR_PHYSICS_UPDATE_FAILED'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
1. **Spring levitation** — PD controller computes spring force each frame to maintain float distance; stable at any `dt`.
|
||||
2. **Slope handling** — projects velocity onto ground normal; blocks movement up slopes exceeding `maxSlopeAngle`.
|
||||
3. **Step detection** — forward ray + step-up ray distinguish steps from walls for smooth stair climbing.
|
||||
4. **Backend abstraction** — `PhysicsBackend` interface decouples math from physics engine; `NullBackend` for unit tests.
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
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`.
|
||||
Reference in New Issue
Block a user