gale.physics package
gale.physics: 2D physics for gale games, backed by pymunk/Chipmunk2D (never exposed in the public API — everything here works in plain pixel units and gale’s own vocabulary) plus a lightweight scene graph for organizing physics entities.
See docs/examples/physics.rst for a walkthrough.
Author: Alejandro Mujica (aledrums@gmail.com)
- class gale.physics.Body(pm_body, world, body_type, pixels_per_meter)[source]
Bases:
objectA physics body, in pixel units. Wraps a single pymunk body (never exposed) plus its fixtures. Created through World.create_static_body/ create_dynamic_body/create_kinematic_body, never directly.
Usage example:
player = world.create_dynamic_body(100, 50, CircleShape(radius=10)) player.apply_impulse(0, -400) # jump
# Every frame, after world.update(dt): pygame.draw.circle(surface, “white”, player.position, 10)
- Parameters:
pm_body (Any)
world (Any)
body_type (int)
pixels_per_meter (float)
- property position: Vector2
- property angle: float
- property velocity: Vector2
- property angular_velocity: float
- set_velocity(vx, vy)[source]
- Parameters:
vx (float) – Horizontal velocity, in pixels per second.
vy (float) – Vertical velocity, in pixels per second.
- Return type:
None
- apply_force(fx, fy)[source]
Apply a continuous force at this body’s center of mass (call every frame while the force should be acting, such as a thruster).
- Parameters:
fx (float) – Horizontal force.
fy (float) – Vertical force.
- Return type:
None
- apply_torque(torque)[source]
- Parameters:
torque (float) – The torque to apply this step.
- Return type:
None
- apply_impulse(ix, iy)[source]
Apply an instantaneous change in momentum at this body’s center of mass (a one-off “kick”, such as a jump).
- Parameters:
ix (float) – Horizontal impulse.
iy (float) – Vertical impulse.
- Return type:
None
- add_circle(shape)[source]
- Parameters:
shape (CircleShape) – The circle fixture to attach to this body.
- Return type:
None
- add_box(shape)[source]
- Parameters:
shape (BoxShape) – The box fixture to attach to this body.
- Return type:
None
- add_polygon(shape)[source]
- Parameters:
shape (PolygonShape) – The polygon fixture to attach to this body.
- Return type:
None
- property touching_bodies: List[Body]
Every other Body currently in contact with this one (a cheap way to answer “is this body resting on something,” with no event bookkeeping needed). Includes sensor overlaps, same as on_collision_begin/on_collision_end.
- Type:
returns
- set_damping(linear_damping=0.0, angular_damping=0.0)[source]
Slow this body’s linear/angular velocity down over time, e.g. to approximate air resistance — every fixed_update(), velocity is scaled by 1 / (1 + fixed_timestep * damping). 0 (the default for every new Body) means no damping at all.
- Parameters:
linear_damping (float) – Damping applied to velocity. The default value is 0.0.
angular_damping (float) – Damping applied to angular_velocity. The default value is 0.0.
- Return type:
None
- class gale.physics.BodyType[source]
Bases:
objectIdentifies how a Body is simulated.
STATIC: never moves, infinite mass. Use it for the ground, walls, and any other fixed level geometry.
DYNAMIC: fully simulated, affected by gravity, forces, and collisions. Use it for anything that should move and react realistically, such as a player or a projectile.
KINEMATIC: moves exactly according to the velocity you set on it, is not affected by gravity/forces/collisions with other bodies, but does push dynamic bodies resting on it. Use it for moving platforms, elevators, or anything else whose motion is scripted rather than simulated.
- STATIC: int = 0
- DYNAMIC: int = 1
- KINEMATIC: int = 2
- class gale.physics.BoxShape(width, height, density=1.0, friction=0.3, restitution=0.0, is_sensor=False, offset=(0, 0))[source]
Bases:
objectA rectangular fixture.
Usage example:
body.add_box(BoxShape(width=32, height=32))
- Parameters:
width (float)
height (float)
density (float)
friction (float)
restitution (float)
is_sensor (bool)
offset (Tuple[float, float])
- class gale.physics.CircleShape(radius, density=1.0, friction=0.3, restitution=0.0, is_sensor=False, offset=(0, 0))[source]
Bases:
objectA circular fixture.
Usage example:
body.add_circle(CircleShape(radius=8, friction=0.4, restitution=0.6))
- Parameters:
radius (float)
density (float)
friction (float)
restitution (float)
is_sensor (bool)
offset (Tuple[float, float])
- class gale.physics.Joint(pm_constraints)[source]
Bases:
objectBase class for a constraint between two bodies. Created through World.create_revolute_joint/create_wheel_joint, never directly.
- Parameters:
pm_constraints (List[Any])
- class gale.physics.Node(x=0, y=0, body=None, children=None)[source]
Bases:
objectA lightweight scene graph node for organizing physics entities: every node has an absolute position (matching how everything else in gale already works), optionally owns a Body (in which case its position/angle are pulled from the body every update, since physics is the source of truth for anything simulated), and can have children for grouping/attachment — a decorative or dependent node that should follow its parent’s body around, or just a way to destroy a group of related nodes together.
This mirrors gale.ui.Container’s parent/children/add_order-render pattern, applied to physics entities instead of widgets.
Usage example:
chassis_node = Node(body=chassis_body) exhaust_node = Node(offset=(-20, 0)) # a decoration, no body of its own chassis_node.add_child(exhaust_node)
# Every frame, after world.update(dt): chassis_node.update(dt) chassis_node.render(surface)
- add_child(node)[source]
- Parameters:
node (Node) – The child to add. Rendered/updated after every previously added child (add-order is z-order, as in gale.ui.Container).
- Return type:
None
- property world_position: Vector2
This node’s absolute position. A node with a body already has an absolute position (physics bodies are always in world coordinates); otherwise, this composes every ancestor’s position with this node’s own offset.
- Type:
returns
- class gale.physics.PolygonShape(points, density=1.0, friction=0.3, restitution=0.0, is_sensor=False)[source]
Bases:
objectAn arbitrary convex polygon fixture.
Usage example:
body.add_polygon(PolygonShape([(0, -10), (10, 10), (-10, 10)]))
- Parameters:
points (List[Tuple[float, float]])
density (float)
friction (float)
restitution (float)
is_sensor (bool)
- class gale.physics.RevoluteJoint(pivot, motor, limit, body_a, body_b)[source]
Bases:
JointA pin joint: constrains two bodies to rotate around a shared point, optionally motorized and/or limited to an angle range.
Usage example:
joint = world.create_revolute_joint(anchor_body, arm, (100, 50)) joint.enable_motor = True joint.motor_speed = 2.0 joint.max_motor_torque = 500
joint.enable_limit = True joint.lower_angle = -0.5 joint.upper_angle = 0.5
- Parameters:
pivot (Any)
motor (Any)
limit (Any)
body_a (Any)
body_b (Any)
- property angle: float
- property motor_speed: float
- property max_motor_torque: float
- property enable_motor: bool
- property enable_limit: bool
- property lower_angle: float
- property upper_angle: float
- class gale.physics.WheelJoint(groove, spring, motor, mass, frequency, damping_ratio)[source]
Bases:
JointA wheel with suspension: constrains a body to slide along an axis relative to another (the suspension, via a groove joint plus a damped spring), plus an unconstrained rotation (the wheel spinning), optionally motorized.
Usage example:
- joint = world.create_wheel_joint(
chassis, wheel, wheel.position, frequencyHz=4, dampingRatio=0.7
) joint.enable_motor = True joint.motor_speed = -10 # drive forward joint.max_motor_torque = 800
- Parameters:
groove (Any)
spring (Any)
motor (Any)
mass (float)
frequency (float)
damping_ratio (float)
- property motor_speed: float
- property max_motor_torque: float
- property enable_motor: bool
- property frequency: float
- property damping_ratio: float
- class gale.physics.World(gravity=(0, 900), pixels_per_meter=30.0, fixed_timestep=0.016666666666666666)[source]
Bases:
objectA physics simulation, in pixel units. Wraps a pymunk space (never exposed) and every Body/Joint created in it.
Follows the same update/fixed_update split Unity uses to decouple the game loop’s real, variable dt from the fixed timestep the solver needs for stable results: call update(dt) once a frame, same as everything else in gale; it accumulates dt and calls fixed_update() (a real simulation step) as many times as needed to consume it. fixed_update() is itself a public method, useful for tests (or the rare game) that want to drive the simulation one fixed step at a time directly.
Usage example:
world = World(gravity=(0, 900)) ground = world.create_static_body(200, 280, BoxShape(400, 20)) ball = world.create_dynamic_body(200, 50, CircleShape(radius=10))
# In your state: def update(self, dt: float) -> None:
self.world.update(dt)
- Parameters:
gravity (Tuple[float, float])
pixels_per_meter (float)
fixed_timestep (float)
- create_static_body(x, y, shape=None)[source]
- Parameters:
x (float) – Initial x position, in pixels.
y (float) – Initial y position, in pixels.
shape (Any | None) – A CircleShape/BoxShape/PolygonShape to attach immediately. The default value is None, so fixtures can be added later via Body.add_circle/add_box/add_polygon.
- Returns:
The new Body.
- Return type:
- create_dynamic_body(x, y, shape=None)[source]
- Parameters:
x (float) – Initial x position, in pixels.
y (float) – Initial y position, in pixels.
shape (Any | None) – A CircleShape/BoxShape/PolygonShape to attach immediately. The default value is None, so fixtures can be added later via Body.add_circle/add_box/add_polygon.
- Returns:
The new Body.
- Return type:
- create_kinematic_body(x, y, shape=None)[source]
- Parameters:
x (float) – Initial x position, in pixels.
y (float) – Initial y position, in pixels.
shape (Any | None) – A CircleShape/BoxShape/PolygonShape to attach immediately. The default value is None, so fixtures can be added later via Body.add_circle/add_box/add_polygon.
- Returns:
The new Body.
- Return type:
- destroy_body(body)[source]
- Parameters:
body (Body) – The Body to remove from this World.
- Return type:
None
- create_revolute_joint(body_a, body_b, anchor, **options)[source]
- Parameters:
body_a (Body) – The first body.
body_b (Body) – The second body.
anchor (Tuple[float, float]) – The pivot point, in pixels, in world coordinates.
options (Any) – enableMotor/motorSpeed/maxMotorTorque and enableLimit/lowerAngle/upperAngle (or their snake_case equivalents) — same meaning as Box2D’s revolute joint, applied at creation. Every one of them is also a settable property on the returned RevoluteJoint for changing later.
- Returns:
The new RevoluteJoint.
- Return type:
- create_wheel_joint(body_a, body_b, anchor, axis=(0, 1), **options)[source]
- Parameters:
body_a (Body) – The chassis (or other body the wheel is attached to).
body_b (Body) – The wheel.
anchor (Tuple[float, float]) – The suspension’s attachment point, in pixels, in world coordinates.
axis (Tuple[float, float]) – The suspension’s movement axis, in body_a’s local frame. The default value is (0, 1) (straight up/down).
options (Any) – frequencyHz/dampingRatio (suspension spring tuning), enableMotor/motorSpeed/maxMotorTorque, and enableLimit/lowerTranslation/upperTranslation (suspension travel bounds, in pixels along axis; unbounded unless enableLimit is set) — same meaning as Box2D’s wheel joint, applied at creation. The motor options are also settable properties on the returned WheelJoint for changing later; the translation limit is creation-only.
- Returns:
The new WheelJoint.
- Return type:
- destroy_joint(joint)[source]
- Parameters:
joint (Joint) – The Joint to remove from this World.
- Return type:
None