simvx.core.physics.engine

Physics engine — rigid body dynamics, collision response, joints, raycasting.

Pure-Python physics built on numpy. Integrates with the node lifecycle via physics_process(dt) and the existing collision shapes from simvx.core.collision.

Architecture: PhysicsServer (singleton) manages bodies and steps the simulation. RigidBody2D/3D nodes register themselves with the server on enter_tree and unregister on exit_tree. The SceneTree’s physics tick calls physics_process on all nodes, which triggers force integration.

Module Contents

Classes

PhysicsMaterial

Surface properties for physics bodies.

BodyMode

Physics body simulation mode.

Contact

A single collision contact between two bodies.

PhysicsServer

Central physics simulation manager.

RigidBody3D

3D rigid body with full physics simulation.

RigidBody2D

2D rigid body with full physics simulation.

StaticBody3D

Immovable 3D physics body. Used for floors, walls, and obstacles.

StaticBody2D

Immovable 2D physics body. Used for platforms and walls.

KinematicBody3D

3D body moved by code that participates in collisions.

KinematicBody2D

2D body moved by code that participates in collisions.

Joint2D

Base class for 2D physics joints.

Joint3D

Base class for 3D physics joints.

PinJoint2D

Pin joint – constrains two 2D bodies to maintain a fixed distance.

PinJoint3D

Pin joint – constrains two 3D bodies to maintain a fixed distance.

HingeJoint3D

Hinge joint – constrains two 3D bodies to rotate around a shared axis.

RayCast3D

Persistent ray query node for 3D physics.

RayCast2D

Persistent ray query node for 2D physics.

Data

API

simvx.core.physics.engine.log[source]

‘getLogger(…)’

simvx.core.physics.engine.__all__

[‘PhysicsMaterial’, ‘BodyMode’, ‘PhysicsServer’, ‘RigidBody2D’, ‘RigidBody3D’, ‘StaticBody2D’, ‘Stat…

class simvx.core.physics.engine.PhysicsMaterial[source]

Surface properties for physics bodies.

Attributes: friction: Coefficient of friction [0..1]. Higher = more grip. restitution: Coefficient of restitution [0..1]. 1 = perfectly elastic bounce. density: Mass per unit volume. Used for auto-computing mass from shape volume.

friction: float

0.5

restitution: float

0.3

density: float

1.0

class simvx.core.physics.engine.BodyMode[source]

Bases: enum.IntEnum

Physics body simulation mode.

Initialization

Initialize self. See help(type(self)) for accurate signature.

DYNAMIC

0

KINEMATIC

1

STATIC

2

__abs__()
__add__()
__and__()
__bool__()
__ceil__()
__delattr__()
__dir__()
__divmod__()
__eq__()
__float__()
__floor__()
__floordiv__()
__format__()
__ge__()
__getattribute__()
__getnewargs__()
__getstate__()
__gt__()
__hash__()
__index__()
__int__()
__invert__()
__le__()
__lshift__()
__lt__()
__mod__()
__mul__()
__ne__()
__neg__()
__new__()
__or__()
__pos__()
__pow__()
__radd__()
__rand__()
__rdivmod__()
__reduce__()
__reduce_ex__()
__repr__()
__rfloordiv__()
__rlshift__()
__rmod__()
__rmul__()
__ror__()
__round__()
__rpow__()
__rrshift__()
__rshift__()
__rsub__()
__rtruediv__()
__rxor__()
__setattr__()
__sizeof__()
__str__()
__sub__()
__subclasshook__()
__truediv__()
__trunc__()
__xor__()
as_integer_ratio()
bit_count()
bit_length()
conjugate()
class denominator
class imag
is_integer()
class numerator
class real
to_bytes()
__deepcopy__(memo)
__copy__()
name()
value()
class simvx.core.physics.engine.Contact[source]

A single collision contact between two bodies.

Attributes: body_a: First body in the collision pair. body_b: Second body in the collision pair. normal: Contact normal pointing from A to B (normalized). point: Contact point in world space. depth: Penetration depth (positive when overlapping).

body_a: Any

None

body_b: Any

None

normal: numpy.ndarray

None

point: numpy.ndarray

None

depth: float

None

class simvx.core.physics.engine.PhysicsServer(cell_size: float = 2.0)[source]

Central physics simulation manager.

Manages all physics bodies, runs broadphase/narrowphase collision detection, resolves contacts with impulse-based response, and provides raycasting.

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

Initialization

classmethod get() simvx.core.physics.engine.PhysicsServer[source]

Return the singleton PhysicsServer, creating it if needed.

classmethod reset() None[source]

Reset the singleton (useful for tests).

set_gravity(gravity) None[source]

Set global gravity vector.

Args: gravity: Gravity as Vec3, tuple, or ndarray. Default (0, -9.8, 0).

property gravity: numpy.ndarray
add_body(body) None[source]

Register a physics body node with the server.

remove_body(body) None[source]

Unregister a physics body from the server.

add_joint(joint) None[source]

Register a joint constraint with the server for solving during step().

remove_joint(joint) None[source]

Unregister a joint constraint from the server.

step(dt: float) None[source]

Advance the physics simulation by dt seconds.

This performs:

  1. Sync positions from nodes

  2. Apply gravity and forces → integrate velocities

  3. Broadphase + narrowphase collision detection

  4. Iterative impulse-based collision response

  5. Integrate positions

  6. Write results back to nodes

  7. Emit body_entered/body_exited signals

raycast(origin, direction, max_dist: float = 1000.0, layer_mask: int = 4294967295) list[simvx.core.collision.RayHit][source]

Cast a ray through the physics world.

Args: origin: Ray start position (Vec3, tuple, or ndarray). direction: Ray direction (will be normalized). max_dist: Maximum ray length. layer_mask: Bitmask to filter bodies by collision layer.

Returns: List of RayHit sorted by distance.

get_overlapping_bodies(body) list[source]

Return all bodies currently overlapping the given body.

property body_count: int
class simvx.core.physics.engine.RigidBody3D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_3d.node3d.Node3D

3D rigid body with full physics simulation.

Supports dynamic, kinematic, and static modes. Collision shapes are set via set_collision_shape() or auto-detected from child nodes.

Signals: body_entered: Emitted when another body starts overlapping. body_exited: Emitted when a previously overlapping body separates.

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

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

Initialization

mass

‘Property(…)’

gravity_scale

‘Property(…)’

linear_damp

‘Property(…)’

angular_damp

‘Property(…)’

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
physics_process(dt: float) None[source]

Called each physics tick. Override for custom behavior.

The PhysicsServer handles integration; this hook runs after forces are accumulated but before the server step.

apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.RigidBody2D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_2d.node2d.Node2D

2D rigid body with full physics simulation.

Same API as RigidBody3D but works in 2D space (x, y). Angular velocity is a scalar (rotation around the z-axis).

Example: class Ball2D(RigidBody2D): mass = Property(1.0)

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

Initialization

mass

‘Property(…)’

gravity_scale

‘Property(…)’

linear_damp

‘Property(…)’

angular_damp

‘Property(…)’

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
physics_process(dt: float) None[source]
apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.StaticBody3D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_3d.node3d.Node3D

Immovable 3D physics body. Used for floors, walls, and obstacles.

Has infinite effective mass — never moved by forces or collisions, but other bodies collide against it normally.

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

Initialization

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.StaticBody2D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_2d.node2d.Node2D

Immovable 2D physics body. Used for platforms and walls.

Same behavior as StaticBody3D but in 2D space.

Initialization

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.KinematicBody3D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_3d.node3d.Node3D

3D body moved by code that participates in collisions.

Not affected by gravity or forces, but pushes dynamic bodies. Use move_and_collide() for movement with collision detection.

Example: class Platform(KinematicBody3D): def physics_process(self, dt): self.linear_velocity = Vec3(0, math.sin(time) * 2, 0)

Initialization

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
move_and_collide(velocity, dt: float = 1.0) simvx.core.physics.engine.Contact | None[source]

Move by velocity*dt and return the first contact, or None.

physics_process(dt: float) None[source]
apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.KinematicBody2D(**kwargs)[source]

Bases: simvx.core.physics.engine._PhysicsBodyMixin, simvx.core.nodes_2d.node2d.Node2D

2D body moved by code that participates in collisions.

Same behavior as KinematicBody3D but in 2D space.

Initialization

collision_layer

‘Property(…)’

collision_mask

‘Property(…)’

enter_tree() None[source]
exit_tree() None[source]
move_and_collide(velocity, dt: float = 1.0) simvx.core.physics.engine.Contact | None[source]

Move by velocity*dt and return the first contact, or None.

physics_process(dt: float) None[source]
apply_force(force, position=None) None
apply_impulse(impulse, position=None) None
apply_torque(torque) None
set_collision_shape(shape: simvx.core.collision.CollisionShape) None
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.Joint2D(body_a=None, body_b=None, **kwargs)[source]

Bases: simvx.core.nodes_2d.node2d.Node2D

Base class for 2D physics joints.

Connects two physics bodies with a constraint. Registers with the PhysicsServer so constraints are solved during the physics step, between velocity integration and position integration.

Attributes: body_a: First connected body (set after adding to tree). body_b: Second connected body (set after adding to tree).

Initialization

enter_tree() None[source]
exit_tree() None[source]
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.Joint3D(body_a=None, body_b=None, **kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

Base class for 3D physics joints.

Connects two physics bodies with a constraint. Registers with the PhysicsServer so constraints are solved during the physics step.

Initialization

enter_tree() None[source]
exit_tree() None[source]
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.PinJoint2D(body_a=None, body_b=None, distance: float = 0.0, stiffness: float = 1.0, damping: float = 0.5, bias_factor: float = 0.3, **kwargs)[source]

Bases: simvx.core.physics.engine.Joint2D

Pin joint – constrains two 2D bodies to maintain a fixed distance.

Uses velocity-level constraint solving with Baumgarte stabilization. Solved by the PhysicsServer during its step, not in physics_process.

Attributes: distance: Rest distance between anchors. 0 = compute from initial positions. stiffness: Constraint stiffness [0..1]. 1 = rigid, lower = springy. damping: Velocity damping factor for the constraint. bias_factor: Baumgarte stabilization factor (higher = faster correction).

Initialization

enter_tree() None
exit_tree() None
z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.PinJoint3D(body_a=None, body_b=None, distance: float = 0.0, stiffness: float = 1.0, damping: float = 0.5, bias_factor: float = 0.3, **kwargs)[source]

Bases: simvx.core.physics.engine.Joint3D

Pin joint – constrains two 3D bodies to maintain a fixed distance.

Uses velocity-level constraint solving with Baumgarte stabilization. Solved by the PhysicsServer during its step.

Attributes: distance: Rest distance. 0 = auto-compute from initial positions. stiffness: Constraint stiffness [0..1]. damping: Velocity damping along the constraint axis. bias_factor: Baumgarte stabilization factor.

Initialization

enter_tree() None
exit_tree() None
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.HingeJoint3D(body_a=None, body_b=None, axis=None, distance: float = 0.0, stiffness: float = 1.0, damping: float = 0.5, bias_factor: float = 0.3, angular_limit_min: float = 0.0, angular_limit_max: float = 0.0, **kwargs)[source]

Bases: simvx.core.physics.engine.Joint3D

Hinge joint – constrains two 3D bodies to rotate around a shared axis.

Maintains a fixed distance in the hinge plane and restricts motion to rotation around a single axis. Two-phase solver:

  1. _solve_constraint (pre-integration): velocity-level Baumgarte correction constrains velocity to be tangential and in the hinge plane.

  2. _position_correct (post-integration): hard position projection snaps the body back to the correct distance on the constraint manifold.

Attributes: axis: Hinge rotation axis in world space (normalized). distance: Rest distance between body centres. stiffness: Position constraint stiffness. damping: Velocity damping. bias_factor: Baumgarte stabilization factor. angular_limit_min: Minimum angle in degrees (0 = no limit). angular_limit_max: Maximum angle in degrees (0 = no limit).

Initialization

enter_tree() None
exit_tree() None
render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
process(dt: float) None
physics_process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.RayCast3D(target_position=None, collision_mask: int = 4294967295, enabled: bool = True, **kwargs)[source]

Bases: simvx.core.nodes_3d.node3d.Node3D

Persistent ray query node for 3D physics.

Casts a ray from the node’s global position towards target_position (in local space) each physics frame and caches the result. Useful for ground detection, line-of-sight checks, lasers, etc.

Example: ray = RayCast3D(target_position=Vec3(0, -2, 0)) player.add_child(ray) # Later, in physics_process: if ray.is_colliding(): print(“Hit:”, ray.get_collider(), “at”, ray.get_collision_point())

Initialization

target_position

‘Property(…)’

collision_mask

‘Property(…)’

enabled

‘Property(…)’

gizmo_colour

‘Property(…)’

get_gizmo_lines() list[tuple[simvx.core.math.types.Vec3, simvx.core.math.types.Vec3]][source]

Return ray line with arrowhead from position to target in world space.

physics_process(dt: float) None[source]
is_colliding() bool[source]

True if the ray hit something on the last physics frame.

get_collider() simvx.core.nodes_3d.node3d.Node3D | None[source]

Return the first body hit, or None.

get_collision_point() simvx.core.math.types.Vec3[source]

Return the world-space collision point of the first hit.

get_collision_normal() simvx.core.math.types.Vec3[source]

Return the collision normal (stub — RayHit lacks normal data).

get_all_hits() list[simvx.core.collision.RayHit][source]

Return all hits from the last cast, sorted by distance.

render_layer

‘Property(…)’

property position
property rotation: simvx.core.math.types.Quat
property scale
property rotation_degrees: simvx.core.math.types.Vec3
property world_position: simvx.core.math.types.Vec3
property world_rotation: simvx.core.math.types.Quat
property world_scale: simvx.core.math.types.Vec3
property forward: simvx.core.math.types.Vec3
property right: simvx.core.math.types.Vec3
property up: simvx.core.math.types.Vec3
translate(offset: tuple[float, float, float] | numpy.ndarray)
translate_global(offset: tuple[float, float, float] | numpy.ndarray)
rotate(axis: tuple[float, float, float] | numpy.ndarray, angle: float)
rotate_x(angle: float)
rotate_y(angle: float)
rotate_z(angle: float)
look_at(target: tuple[float, float, float] | numpy.ndarray, up=None)
set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
wrap_bounds(bounds: tuple[float, float, float] | numpy.ndarray, margin: float = 1.0)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.engine.RayCast2D(target_position=None, collision_mask: int = 4294967295, enabled: bool = True, **kwargs)[source]

Bases: simvx.core.nodes_2d.node2d.Node2D

Persistent ray query node for 2D physics.

Casts a ray from the node’s global position towards target_position (in local space) each physics frame and caches the result.

Example: ray = RayCast2D(target_position=Vec2(0, 100)) player.add_child(ray) if ray.is_colliding(): print(“Hit:”, ray.get_collider())

Initialization

target_position

‘Property(…)’

collision_mask

‘Property(…)’

enabled

‘Property(…)’

gizmo_colour

‘Property(…)’

get_gizmo_lines() list[tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2]][source]

Return ray line with arrowhead from position to target in world space.

physics_process(dt: float) None[source]
is_colliding() bool[source]

True if the ray hit something on the last physics frame.

get_collider() simvx.core.nodes_2d.node2d.Node2D | None[source]

Return the first body hit, or None.

get_collision_point() simvx.core.math.types.Vec2[source]

Return the world-space collision point of the first hit.

get_collision_normal() simvx.core.math.types.Vec2[source]

Return the collision normal (stub — RayHit lacks normal data).

get_all_hits() list[simvx.core.collision.RayHit][source]

Return all hits from the last cast, sorted by distance.

z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property position: simvx.core.math.types.Vec2
property rotation: float
property rotation_degrees: float
property scale: simvx.core.math.types.Vec2
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property process_mode: simvx.core.descriptors.ProcessMode
reset_error() None
add_child(node: simvx.core.node.Node) simvx.core.node.Node
remove_child(node: simvx.core.node.Node)
reparent(new_parent: simvx.core.node.Node)
get_node(path: str) simvx.core.node.Node
find_child(name: str, recursive: bool = False) simvx.core.node.Node | None
find(node_type: type, recursive: bool = True) simvx.core.node.Node | None
find_all(node_type: type, recursive: bool = True) list
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
ready() None
enter_tree() None
exit_tree() None
process(dt: float) None
draw(renderer) None
input_event(event: simvx.core.events.InputEvent) None
input(event: simvx.core.events.TreeInputEvent) None
unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
clear_children()
destroy()
property app
property tree: simvx.core.scene_tree.SceneTree
get_tree() simvx.core.scene_tree.SceneTree
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()