# Physics Engine SimVX provides rigid body dynamics, impulse-based collision response, and constraint joints on top of the collision detection system described in {doc}`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`. ```python 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: ```python 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: ```python 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: ```python 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: ```python 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). ```python # 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): ```python 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: ```python 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 ```python 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 {py:mod}`simvx.core.physics.engine` for the complete physics engine API.