feat(engine): 2D collision / physics-lite — Body + Collider + esys_move (#65)
Phase 0 of the gameplay-controller work (#57): turn the static Collision.* overlap tests into a real physics-lite moving-body-with-collision layer that the platformer / shooter / NPC-AI / RPG families build on. New engine-owned system esys_move (runtime/native/systems_move.ludic), spliced and Update-phase-registered when a game declares `Body` (parse.ludic), standing entirely on the reflection ABI like the SpriteAnim / Motion / Light2D systems: - Body { vx, vy, gravity, max_fall, rx, ry, policy, on_ground, hit_wall, hit_ceiling } — Q16.16 velocity + engine-owned sub-pixel accumulators. - Collider { w, h, offx, offy, is_trigger, one_way, layer, mask, hit, entered, exited } — AABB shape off Position, layer/mask filtering, one-way + triggers. - Solids { tile, wall, oneway } — optional config entity enabling the tile-grid broadphase over the Map.* tilemap. esys_move integrates velocity + gravity, then resolves per-axis swept AABB against both solid Collider entities and the tile grid (no tunneling), handles one-way platforms (block only a downward landing), reports trigger/sensor overlaps without resolving, and sets on_ground / hit_wall / hit_ceiling for a controller to poll. No float, no hidden singletons, no runtime dispatch — integer + deterministic, so replay / lockstep / world_save hold. Contact is surfaced as polled flags (the SpriteAnim.event_fired shape), so a game raises its own CollisionResolved / TriggerEntered events with no engine coupling. Two self-asserting examples (entity + tile broadphase) wired into `x test`; docs added to the collision section. A build with no Body compiles byte-for-byte the same (bootstrap fixpoint + all golden renders unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6c6dfae235
commit
0b149a6876
7 changed files with 17645 additions and 16763 deletions
|
|
@ -5,3 +5,11 @@ order: 6
|
|||
---
|
||||
|
||||
2D overlap tests on integer coordinates (pixels or tiles). Rectangles are <code>(x, y, w, h)</code> from the top-left; circles are <code>(x, y, r)</code>. Each returns a bool. Squared distances are computed in 64-bit so large coordinates never overflow.
|
||||
|
||||
These are static boolean tests. For a *moving* body with real collision resolution, the engine ships a physics-lite movement system built on the same integer math — declare the well-known components and the engine advances them for you each frame, no handler wired:
|
||||
|
||||
- <code>property Body { vx, vy, gravity, max_fall, rx, ry, policy, on_ground, hit_wall, hit_ceiling }</code> — velocity/accel state in Q16.16 fixed-point (<code>vx</code>/<code>vy</code>/<code>gravity</code>/<code>max_fall</code>), engine-owned sub-pixel accumulators (<code>rx</code>/<code>ry</code>), an integration <code>policy</code> (<code>0</code> platformer with gravity, <code>1</code> top-down), and the derived contact flags the engine sets each frame.
|
||||
- <code>property Collider { w, h, offx, offy, is_trigger, one_way, layer, mask, hit, entered, exited }</code> — an AABB shape offset from the entity's <code>Position { x, y }</code>, with layer/mask filtering, one-way-platform and trigger flags, and per-frame trigger outputs.
|
||||
- <code>property Solids { tile, wall, oneway }</code> — an optional single config entity that turns on the tile-grid broadphase over the <a href="map"><code>Map</code></a> tilemap (<code>tile</code> px size, the solid <code>wall</code> glyph, and an optional one-way <code>oneway</code> glyph).
|
||||
|
||||
The engine-owned <code>esys_move</code> system (Update phase) integrates velocity and gravity into a tentative move, then resolves it with a per-axis **swept AABB** — against both solid <code>Collider</code> entities and the tile grid — so a fast body never tunnels through a wall. It handles one-way platforms (which block only a downward landing), reports trigger/sensor overlaps without resolving them, and sets <code>on_ground</code> / <code>hit_wall</code> / <code>hit_ceiling</code> for a controller to read. Everything is integer and deterministic, so movement reproduces exactly under replay, lockstep and <code>world_save</code> snapshots. Contact is surfaced as polled flags (the <code>SpriteAnim.event_fired</code> shape) rather than engine-emitted events, so a game raises its own <code>CollisionResolved</code> / <code>TriggerEntered</code> events from its handler with no coupling. This is the shared foundation the built-in gameplay controllers build on. Related: <a href="grid"><code>Grid</code></a>, <a href="map"><code>Map</code></a>, <a href="motion"><code>Motion</code></a>, <a href="annot-enginesystem"><code>@EngineSystem</code></a>.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue