Physics Engine

SimVX provides rigid body dynamics, impulse-based collision response, and constraint joints on top of the collision detection system described in Collision & Physics. The physics engine runs in the physics_process tick and integrates with the node lifecycle automatically.

PhysicsServer

PhysicsServer is a singleton that manages all physics bodies, steps the simulation, and emits overlap signals. Bodies register themselves on enter_tree and unregister on exit_tree.

from simvx.core.physics.engine import PhysicsServer, Vec3

server = PhysicsServer.get()
server.set_gravity(Vec3(0, -9.8, 0))

Each step(dt) performs: force integration, broadphase/narrowphase detection, iterative impulse solving, position integration, Baumgarte correction, and overlap signal dispatch.

PhysicsMaterial

Surface properties that control bounce and friction:

from simvx.core.physics.engine import PhysicsMaterial

rubber = PhysicsMaterial(friction=0.9, restitution=0.8, density=1.2)
ice = PhysicsMaterial(friction=0.05, restitution=0.1, density=0.9)

Field

Default

Description

friction

0.5

Coefficient of friction [0..1]

restitution

0.3

Bounciness [0..1] – 1 is perfectly elastic

density

1.0

Mass per unit volume

Body Types

RigidBody2D / RigidBody3D

Fully simulated – affected by gravity, forces, and collisions:

from simvx.core.physics.engine import RigidBody3D, PhysicsMaterial
from simvx.core import SphereShape, Property

class Ball(RigidBody3D):
    mass = Property(2.0)
    physics_material = PhysicsMaterial(restitution=0.8)

    def ready(self):
        self.set_collision_shape(SphereShape(radius=0.5))

StaticBody2D / StaticBody3D

Immovable – infinite effective mass, never displaced by forces:

from simvx.core.physics.engine import StaticBody3D
from simvx.core import BoxShape, Vec3

ground = StaticBody3D(position=Vec3(0, -1, 0))
ground.set_collision_shape(BoxShape(half_extents=(50, 1, 50)))

KinematicBody2D / KinematicBody3D

Moved by code – not affected by forces, but pushes dynamic bodies:

from simvx.core.physics.engine import KinematicBody3D
from simvx.core import Vec3
import math

class MovingPlatform(KinematicBody3D):
    def physics_process(self, dt):
        t = self.get_process_time()
        self.linear_velocity = Vec3(0, math.sin(t) * 2, 0)

Forces and Impulses

All body types with the _PhysicsBodyMixin support:

  • apply_force(force, position=None) – Continuous force integrated over the next step. Pass position for off-center torque.

  • apply_impulse(impulse, position=None) – Instantaneous velocity change. Pass position for angular impulse.

  • apply_torque(torque) – Continuous torque (Vec3 for 3D, scalar for 2D).

# Launch a ball upward
ball.apply_impulse(Vec3(0, 20, 0))

# Spin it
ball.apply_torque(Vec3(0, 5, 0))

Signals

  • body_entered(other) – Emitted when a new overlap begins.

  • body_exited(other) – Emitted when a previously overlapping body separates.

Joints

Joints constrain the relative motion of two bodies.

PinJoint2D / PinJoint3D

Maintains a fixed distance between two bodies (distance auto-computed from initial positions if not specified):

from simvx.core.physics.engine import PinJoint3D

joint = PinJoint3D(body_a=ball_a, body_b=ball_b, stiffness=0.9, damping=0.1)

HingeJoint3D

Constrains rotation to a single axis, plus a pin-style distance constraint:

from simvx.core.physics.engine import HingeJoint3D
from simvx.core import Vec3

hinge = HingeJoint3D(
    body_a=door, body_b=frame,
    axis=Vec3(0, 1, 0),          # Rotate around Y
    angular_limit_min=-90.0,
    angular_limit_max=90.0,
)

Bouncing Ball Example

from simvx.core import Node3D, Vec3, SphereShape, BoxShape, Property
from simvx.core.physics.engine import RigidBody3D, StaticBody3D, PhysicsMaterial

class BouncingBall(RigidBody3D):
    mass = Property(1.0)
    physics_material = PhysicsMaterial(restitution=0.9)

    def ready(self):
        self.set_collision_shape(SphereShape(radius=0.5))
        self.position = Vec3(0, 10, 0)

class Floor(StaticBody3D):
    def ready(self):
        self.set_collision_shape(BoxShape(half_extents=(20, 0.5, 20)))
        self.position = Vec3(0, -0.5, 0)

class Scene(Node3D):
    def ready(self):
        self.add_child(BouncingBall())
        self.add_child(Floor())

API Reference

See simvx.core.physics.engine for the complete physics engine API.