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: object

A 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

destroy()[source]

Remove this body (and its fixtures) from its World. Do not use this Body afterwards.

Return type:

None

class gale.physics.BodyType[source]

Bases: object

Identifies 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: object

A 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: object

A 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: object

Base class for a constraint between two bodies. Created through World.create_revolute_joint/create_wheel_joint, never directly.

Parameters:

pm_constraints (List[Any])

destroy(pm_space)[source]

Internal — use World.destroy_joint instead.

Parameters:

pm_space (Any)

Return type:

None

class gale.physics.Node(x=0, y=0, body=None, children=None)[source]

Bases: object

A 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)

Parameters:
  • x (float)

  • y (float)

  • body (Body | None)

  • children (Sequence[Node] | None)

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

remove_child(node)[source]
Parameters:

node (Node) – The child to remove.

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

update(dt)[source]
Parameters:

dt (float) – Time elapsed, in seconds, since the last call.

Return type:

None

render(surface)[source]
Parameters:

surface (Surface) – The surface to draw this node (and its children) on. The base Node itself draws nothing — subclasses/games draw whatever they want at self.world_position.

Return type:

None

class gale.physics.PolygonShape(points, density=1.0, friction=0.3, restitution=0.0, is_sensor=False)[source]

Bases: object

An 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: Joint

A 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: Joint

A 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: object

A 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:

Body

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:

Body

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:

Body

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:

RevoluteJoint

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:

WheelJoint

destroy_joint(joint)[source]
Parameters:

joint (Joint) – The Joint to remove from this World.

Return type:

None

on_collision_begin(callback)[source]
Parameters:

callback (Callable[[Body, Body], None]) – Called with (body_a, body_b) when two fixtures start touching. At least one of the two must not be a sensor for physical collision response; either or both may be sensors for overlap-only detection.

Return type:

None

on_collision_end(callback)[source]
Parameters:

callback (Callable[[Body, Body], None]) – Called with (body_a, body_b) when two fixtures that were touching stop touching.

Return type:

None

update(dt)[source]

Call once a frame with the real, variable elapsed time. Steps the simulation forward by calling fixed_update() as many times as the accumulated time covers.

Parameters:

dt (float) – Time elapsed, in seconds, since the last call.

Return type:

None

fixed_update()[source]

Advance the simulation by exactly one fixed_timestep.

Return type:

None

render_debug(surface, color=(0, 255, 0))[source]

Draw every fixture’s outline directly onto surface, with no assets — a debugging aid for building a level.

Parameters:
  • surface (Surface) – The surface to draw on.

  • color – The outline color. The default value is (0, 255, 0).

Return type:

None

Submodules