Keel’s physics layer is a thin ECS bridge: theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/VKSFY/keel/llms.txt
Use this file to discover all available pages before exploring further.
Physics2D and Physics3D objects own the actual simulation (a pymunk Space and a pybullet DIRECT client, respectively). Every Phase.POST_UPDATE tick the bridge runs four phases in order:
sync_to_physics— reads ECS components (Transform2D/3D,RigidBody2D/3D,Collider2D/3D) and creates, updates, or removes the corresponding physics objects.step(dt)— advances the solver bydtseconds.sync_from_physics— writes the solved positions and velocities back into the ECS structured arrays in-place._emit_collisions— drains the collision buffer and callsworld.emit(CollisionEvent2D/3D(...)).
2D Physics
setup_physics_2d
Physics2D bridge, inserts it as a world resource, and registers the Phase.POST_UPDATE system. Idempotent — subsequent calls return the same Physics2D instance.
Requires: pymunk (pip install pymunk).
The Keel
App instance. setup_physics_2d attaches the bridge to app._keel_physics_2d and wires a Phase.POST_UPDATE system.Horizontal gravity component in pixels/second². Typically
0.0 for standard top-down or side-scrolling games.Vertical gravity in pixels/second².
-980.0 matches Earth gravity in centimeter-gram-second units (pymunk’s default scale). Use 0.0 for top-down games.Physics2D
Physics2D wraps a single pymunk Space. It is inserted as a world resource and can be injected into any system for imperative control.
Methods
Overwrite the linear velocity of
entity_id’s pymunk body. Also mirrors the new values into the RigidBody2D ECS component so the subsequent sync_to_physics does not revert the change. No-op if the entity has no physics body.Teleport the body to
(x, y). Also updates Transform2D in the ECS. Useful for kinematic platforms or respawn points. No-op if the entity has no physics body.Apply a world-space impulse at the body’s center of mass. Impulse = change in momentum (mass × Δvelocity). No-op if the entity has no physics body.
Apply a continuous world-space force at the body’s center of mass. Force is accumulated per-step (unlike impulse which is instantaneous). No-op if the entity has no physics body.
Perform a segment query in the pymunk space between
start and end (each a (float, float) tuple). Returns a list of hit dictionaries sorted nearest-first by alpha, each containing:entity_id: int— the entity whose collider was hitpoint: (float, float)— world-space hit pointnormal: (float, float)— surface normal at the hitalpha: float— fraction along the segment[0, 1]
Remove every body and shape from the pymunk space. Called automatically by the app shutdown hook. Idempotent.
3D Physics
setup_physics_3d
Physics3D bridge backed by a pybullet DIRECT client (no GUI window), inserts it as a world resource, and registers the Phase.POST_UPDATE system. Idempotent.
Requires: pybullet — install with pip install keelpy[physics3d]. Raises ImportError with instructions if pybullet is not installed.
The Keel
App instance.Vertical gravity in meters/second² (SI units). The bridge always sets
gravity_x = 0.0 and gravity_z = 0.0; only Y gravity is configurable through setup_physics_3d.Physics3D
Physics3D wraps a single pybullet DIRECT-mode client. It is inserted as a world resource and can be injected into systems.
pybullet uses fixed substeps internally (
_FIXED_DT = 1/240 s, up to _MAX_SUBSTEPS = 10 per tick). The step(dt) method forwards the frame delta; pybullet accumulates it and steps in fixed increments.Methods
Overwrite the linear velocity of
entity_id’s pybullet body and mirror the values into the RigidBody3D ECS component. No-op if the entity has no physics body.Teleport the body to
(x, y, z) and update Transform3D in the ECS. No-op if the entity has no physics body.Apply a world-space impulse at the body’s center of mass. No-op if the entity has no physics body.
Disconnect the pybullet client. Called automatically by the app shutdown hook. Idempotent.
Integration flow
ECS in → physics objects
sync_to_physics walks all archetypes that have (Transform2D/3D, RigidBody2D/3D, Collider2D/3D). New entities create bodies and shapes. Entities whose body_type changed are rebuilt. Removed entities are cleaned up.Simulation step
step(dt) advances the solver. pymunk uses a single fixed dt per call; pybullet subdivides into _FIXED_DT-sized substeps.Physics out → ECS
sync_from_physics writes the solved position, angle (2D) or position, orientation (3D) back into the Transform and RigidBody structured arrays in-place. STATIC bodies are skipped.