forge3d.Body¶
A handle to a single rigid body in the simulation. Returned by every World.add_* call.
Class reference¶
Body ¶
Handle to a simulated rigid body.
Returned by world.add_box(), world.add_sphere(), etc.
Use properties to read the body's current state and methods to control it.
Examples¶
box = world.add_box(size=(1, 1, 1), position=(0, 0, 5), name="my_box") box.position array([0., 0., 5.]) box.name 'my_box' box.apply_force((0, 0, 10)) # applied on next world.step()
rotation_matrix
property
¶
Body orientation as a 3×3 rotation matrix (world frame).
name
property
writable
¶
Human-readable name assigned at creation. Can be changed at runtime.
is_sleeping
property
¶
True if this body is below the sleep velocity threshold for ≥ 1 s.
shape_params
property
¶
Shape parameters dict (e.g. {'half_extents': array([0.5, 0.5, 0.5])}).
Returns a copy — mutating the dict does not affect the simulation.
friction
property
writable
¶
Coulomb friction coefficient (>= 0). Can be changed at runtime.
restitution
property
writable
¶
Coefficient of restitution [0, 1]. Can be changed at runtime.
linear_damping
property
writable
¶
Linear velocity damping coefficient (per second, >= 0).
Applied automatically each world.step(). A value of 0.1 removes
~10% of linear velocity per second.
angular_damping
property
writable
¶
Angular velocity damping coefficient (per second, >= 0).
Applied automatically each world.step().
collision_layer
property
writable
¶
Bit-field: which layer(s) this body belongs to.
collision_mask
property
writable
¶
Bit-field: which layers this body detects collisions with.
apply_force ¶
Accumulate a world-frame force (N) to apply on the next step.
Forces reset to zero after each :meth:World.step call.
Parameters¶
force : (3,) force vector in Newtons, world frame.
Notes¶
For per-frame forces use this instead of apply_impulse::
body.apply_force(np.array([0, 0, 50])) # 50 N upward thrust
world.step(dt=1/60) # force applied here
apply_torque ¶
Accumulate a world-frame torque (N·m) to apply on the next step.
Torques reset to zero after each :meth:World.step call.
set_position ¶
Instantly teleport this body to position (keeps orientation).
set_orientation ¶
Instantly set orientation (keeps position). quat = [w, x, y, z].
on_collision_begin ¶
Register a callback fired when this body first contacts another.
The callback receives (other: Body, event: CollisionEvent).
Can be used as a decorator::
@player.on_collision_begin
def hit(other, event):
print(f"player hit {other.name}")
on_collision_end ¶
Register a callback fired when this body separates from another.
Usage examples¶
Reading state¶
box = world.add_box(size=(1,1,1), position=(0,0,5), mass=2.0)
print(box.position) # array([0., 0., 5.])
print(box.mass) # 2.0
print(box.is_static) # False
print(box.is_sleeping) # False
Runtime setters (v1.1.0)¶
# Change physics material at runtime
box.friction = 0.05 # ice-slippery (default: 0.5)
box.restitution = 0.95 # very bouncy (default: 0.3)
Velocity damping (v1.1.0)¶
# Exponential decay — dt-corrected, FPS-independent
box.linear_damping = 1.0 # removes 63% of speed per second
box.angular_damping = 2.0 # removes 86% of spin per second
# Applied automatically every world.step()
linear_damping |
Speed remaining after 1 s |
|---|---|
| 0.0 | 100% (no damping) |
| 0.5 | 61% |
| 1.0 | 37% |
| 2.0 | 14% |
Applying forces¶
box.apply_force((0, 0, 50)) # 50 N upward (reset each step)
box.apply_torque((0, 0, 10)) # 10 N·m yaw torque
world.step(dt=1/60)
print(box.velocity)
Runtime name change¶
head = world.add_sphere(radius=0.21, position=(0,0,1.8), name="head")
head.name = "player_head" # settable at any time
print(head.name) # "player_head"
Shape query¶
capsule = world.add_capsule(radius=0.3, half_length=0.9, position=(0,0,2))
print(capsule.shape_type) # "capsule"
print(capsule.shape_params) # {'radius': 0.3, 'half_length': 0.9}
# Rotation as 3×3 matrix (alternative to .orientation quaternion)
R = capsule.rotation_matrix # np.ndarray (3, 3)
Per-body collision callbacks¶
player = world.add_capsule(radius=0.3, half_length=0.9, name="player")
floor = world.add_ground()
@player.on_collision_begin
def hit(other, event):
print(f"player hit {other.name} speed={event.relative_speed:.1f} m/s")
@player.on_collision_end
def left(other, event):
print(f"player separated from {other.name}")
# Callbacks fire automatically inside world.step()
Collision layers¶
from forge3d import CollisionLayer
player = world.add_sphere(radius=0.5, position=(0,0,3), mass=1)
player.collision_layer = CollisionLayer.PLAYER
player.collision_mask = CollisionLayer.mask_for(
CollisionLayer.TERRAIN,
CollisionLayer.ENEMY,
) # collide only with terrain and enemies
ghost = world.add_sphere(radius=1.0, position=(5,0,0), mass=0.1)
ghost.collision_layer = CollisionLayer.NONE # no collision