gale.physics package
gale.physics: 2D physics for gale games, backed by Box2D (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(b2_body, body_type, pixels_per_meter)[source]
Bases:
objectA physics body, in pixel units. Wraps a single Box2D 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:
b2_body (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
- 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(b2_joint)[source]
Bases:
objectBase class for a constraint between two bodies. Created through World.create_revolute_joint/create_wheel_joint, never directly.
- Parameters:
b2_joint (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 (Box2D 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 (at most 8 vertices, Box2D’s own limit).
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(b2_joint)[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
- Parameters:
b2_joint (Any)
- property angle: float
- property motor_speed: float
- property max_motor_torque: float
- property enable_motor: bool
- class gale.physics.WheelJoint(b2_joint)[source]
Bases:
JointA wheel with suspension: constrains a body to move along an axis relative to another (the suspension), 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:
b2_joint (Any)
- 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 Box2D world (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 Box2D’s 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 Box2D 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:
- 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) – Forwarded to Box2D’s wheel joint definition (e.g. frequencyHz, dampingRatio, maxMotorTorque, motorSpeed, enableMotor).
- Returns:
The new WheelJoint.
- Return type:
- destroy_joint(joint)[source]
- Parameters:
joint (Joint) – The Joint to remove from this World.
- Return type:
None