body
FreeBodyEngine.core.physics.body
#
RigidBody2D - the new rigid-body physics node.
Unlike PhysicsBody (the older, simpler "arcade physics" system this
package still exports for backward compatibility - push-out collision
response with no concept of a real constraint solver), a RigidBody2D
is simulated by PhysicsWorld alongside every other body and Joint2D
in the scene, all at once, through a proper sequential-impulse velocity
solver - which is what makes joints, motors, and physically stable
stacking/resting contact possible at all.
BodyType
#
Bases: Enum
What drives a RigidBody2D's motion:
- STATIC: never moves (infinite mass/inertia) - level geometry, walls.
- KINEMATIC: moves only when you set its position/rotation directly (e.g. animating a moving platform) - unaffected by forces/impulses/ collision response, but still pushes DYNAMIC bodies it touches.
- DYNAMIC: fully simulated - forces, gravity, collision response, and joints all apply.
RigidBody2D(position=Vector(), rotation=0.0, body_type=BodyType.DYNAMIC, density=1.0, mass=None, inertia=None, linear_velocity=Vector(0, 0), angular_velocity=0.0, linear_damping=0.05, angular_damping=0.05, gravity_scale=1.0, friction=0.3, restitution=0.0, fixed_rotation=False, is_sensor=False, collision_layer=1, collision_mask=4294967295, allow_sleep=True)
#
Bases: Node2D
A physically-simulated body: requires a sibling Collider2D child
(declared via self.requirements, exactly like PhysicsBody) whose
shape drives both collision detection and (unless overridden) this
body's auto-computed mass/inertia. Every RigidBody2D in a scene is
simulated together by a PhysicsWorld (see world.py) - forces
accumulate here between physics steps, but integration, collision
response, and joint solving all happen there, not on the body itself.
Records every physics parameter, but doesn't compute mass/
inertia yet - that needs this body's Collider2D child, which
isn't available until on_initialize() runs.
mass/inertia, if given, override the auto-derived values from
density and the collider's shape entirely (useful for
gameplay-driven bodies where "realistic" density-based mass would
fight the feel you actually want).
allow_sleep = allow_sleep
instance-attribute
#
angular_damping = angular_damping
instance-attribute
#
angular_velocity = angular_velocity
instance-attribute
#
body_type = body_type
instance-attribute
#
collider = None
instance-attribute
#
collision_layer = collision_layer
instance-attribute
#
collision_mask = collision_mask
instance-attribute
#
density = density
instance-attribute
#
fixed_rotation = fixed_rotation
instance-attribute
#
friction = friction
instance-attribute
#
gravity_scale = gravity_scale
instance-attribute
#
inertia = 0.0
instance-attribute
#
inv_inertia = 0.0
instance-attribute
#
inv_mass = 0.0
instance-attribute
#
is_sensor = is_sensor
instance-attribute
#
is_sleeping = False
instance-attribute
#
linear_damping = linear_damping
instance-attribute
#
linear_velocity = linear_velocity.copy()
instance-attribute
#
mass = 0.0
instance-attribute
#
requirements = ['Collider2D']
instance-attribute
#
restitution = restitution
instance-attribute
#
touching = set()
instance-attribute
#
apply_force(force, world_point=None)
#
Accumulates force, applied at world_point if given
(otherwise at this body's center of mass, producing no torque).
Cleared every physics step after integration - for a constant
force (e.g. a thruster), call this every step, not just once.
apply_impulse(impulse, world_point=None)
#
Immediately changes velocity by impulse * inv_mass (and
angular velocity, if world_point is off-center) - unlike
apply_force(), this is a one-shot velocity change applied right
away, not accumulated for the next integration step. Used
directly by explosions/knockback/etc, and internally by the
constraint solver itself.
apply_torque(torque)
#
Accumulates a pure rotational force, with no linear component.
on_collision_enter(other, contact)
#
Called the first physics step two (non-sensor) bodies start
touching - override to react (damage, sound, sticking). contact
is the core.physics.contact.Manifold for this pair, in this
body's own frame (i.e. contact.normal points from this body
toward other).
on_collision_exit(other)
#
Called the physics step two bodies stop touching, having touched the step before. No-op by default.
on_collision_stay(other, contact)
#
Called every physics step (after the first) two bodies remain touching. No-op by default.
on_initialize()
#
Finds this body's collider child and computes mass/inertia
from it (unless overridden), then derives the inverse values the
solver actually uses - 0 for a STATIC/KINEMATIC body (or, for
inertia, a fixed_rotation one), so multiplying by inv_mass/
inv_inertia anywhere naturally applies zero effect instead of
needing a body-type check at every use site.
on_physics_process()
#
Called once per physics step, before force integration - override to run custom per-step physics logic (e.g. a leg's IK target update).
on_trigger_enter(other)
#
Called the first physics step this body (or other) starts
overlapping a sensor - fired instead of on_collision_enter for
any pair where either body has is_sensor=True, since a sensor
detects overlap without a physical collision response. No-op by
default.
on_trigger_exit(other)
#
Called the physics step this body/other's sensor overlap ends.
velocity_at_point(world_point)
#
The linear velocity of the material point on this body
currently at world_point - its center-of-mass velocity plus the
tangential velocity from rotation about that offset. Needed by
the contact solver (relative velocity at the actual contact point,
not just the two bodies' center velocities, matters once either
body is rotating).
wake()
#
Clears sleeping state and resets the sleep timer - called automatically by force/impulse application, and by the solver when an awake body touches a sleeping one.
A no-op if already awake: apply_force()/apply_impulse() call this unconditionally, including from within the contact solver's own routine impulses (a resting body still gets a small corrective impulse every step, to counteract that same step's gravity - it's what "resting" means). If this reset the sleep timer even for an already-awake body, that alone would keep a perfectly settled body from ever accumulating enough idle time to sleep at all.