simvx.core.physics.root

Role

A PhysicsRoot node carves its sub-branch of the scene tree into a separate, isolated :class:~simvx.core.physics.world.PhysicsWorld. Bodies resolve their world by walking up to the nearest PhysicsRoot ancestor; if there is none, they fall back to a default world (one per scene tree). Nested roots: the innermost wins. This mirrors the scoping pattern the codebase already uses and is the future unit of cross-world parallelism.

The factory used to build a world for a root (and for the default) is pluggable so a backend (BuiltinPhysics now, JoltPhysics later) can be selected without changing the resolution logic. Which backend a root builds is decided by

mod:

simvx.core.physics.backends (precedence: explicit PhysicsRoot(backend=)

project physics_backend setting > auto-discovered native > Builtin); a direct factory= argument is the test escape hatch that bypasses precedence.

Stage 2 status

The new seam is now wired into SceneTree.physics_tick. World ownership is SceneTree-owned: the default world (for bodies with no PhysicsRoot ancestor) lives on the tree as tree.physics_world (lazy, tree-scoped, survives change_scene like the event bus), and each PhysicsRoot registers its own isolated world with the tree on enter and unregisters + drops it on exit, so no worlds leak on teardown.

The sole provisional remnant is the treeless fallback: a single module-level default world used only when :func:resolve_world is given a node with no SceneTree (standalone unit tests that call step() manually).

PhysicsRoot: branch-isolating physics node + node->world resolution.

Module Contents

Classes

PhysicsRoot

A node that owns an isolated :class:PhysicsWorld for its sub-branch.

PhysicsRoot2D

A node that owns an isolated :class:Physics2DWorld for its sub-branch.

Functions

resolve_world

Resolve the :class:PhysicsWorld a node’s bodies belong to.

resolve_world_2d

Resolve the :class:Physics2DWorld a node’s 2D bodies belong to.

Data

API

simvx.core.physics.root.WorldFactory

None

simvx.core.physics.root.WorldFactory2D

None

simvx.core.physics.root.resolve_world(node: simvx.core.node.Node) simvx.core.physics.world.PhysicsWorld[source]

Resolve the :class:PhysicsWorld a node’s bodies belong to.

Walks up from node (inclusive) to the nearest :class:PhysicsRoot ancestor and returns that root’s world. If no PhysicsRoot is found, returns the tree’s default world (node.tree.physics_world), or, for a treeless node, the module-level fallback world.

Args: node: The node whose world to resolve.

Returns: The resolved :class:PhysicsWorld (innermost root wins; tree default otherwise; treeless fallback when the node is not in a tree).

simvx.core.physics.root.resolve_world_2d(node: simvx.core.node.Node) simvx.core.physics.world2d.Physics2DWorld[source]

Resolve the :class:Physics2DWorld a node’s 2D bodies belong to.

The 2D sibling of :func:resolve_world. Walks up from node (inclusive) to the nearest :class:PhysicsRoot2D ancestor and returns that root’s 2D world. If none is found, returns the tree’s default 2D world (node.tree.physics_world_2d), or, for a treeless node, the module-level 2D fallback world.

Args: node: The node whose 2D world to resolve.

Returns: The resolved :class:Physics2DWorld (innermost PhysicsRoot2D wins; tree 2D default otherwise; treeless 2D fallback when not in a tree).

class simvx.core.physics.root.PhysicsRoot(name: str = '', *, gravity: simvx.core.math.Vec3 = _DEFAULT_GRAVITY, backend: str | None = None, factory: simvx.core.physics.root.WorldFactory | None = None, **kwargs: object)[source]

Bases: simvx.core.node.Node

A node that owns an isolated :class:PhysicsWorld for its sub-branch.

Bodies under a PhysicsRoot (with no nearer PhysicsRoot) simulate in this node’s world, fully isolated from the rest of the tree: independent gravity and stepping. Resolve a node’s world with :func:resolve_world.

The world is created lazily on first access via the configured factory (defaults to BuiltinPhysics), so constructing the node is cheap and the backend choice stays in one place.

Stage 2: this root registers its world with the SceneTree on enter so the fixed-step loop (SceneTree.physics_tick) advances it, and unregisters + drops it on exit so nothing leaks across scene swaps or teardown.

Initialization

Initialise the physics root.

Args: name: Node name (defaults to the class name). gravity: Gravity for this root’s isolated world. backend: Explicit backend-name override (arm 1 of the selection precedence: explicit > project physics_backend setting > auto-discovered native > Builtin). None (default) defers to the setting / auto / Builtin arms. An unknown name degrades to the next arm with a warning (a missing optional native backend does not crash the game). factory: Direct backend-factory escape hatch. When given it bypasses the backend precedence entirely and is used verbatim (used by tests to inject a stub world). Defaults to None -> resolve via backend precedence. **kwargs: Forwarded to :class:~simvx.core.node.Node.

property world: simvx.core.physics.world.PhysicsWorld[source]

This root’s isolated world, created lazily on first access.

Built from the explicit factory if one was given, else from the backend resolved by precedence (explicit backend > project setting > auto-discovered native > Builtin).

on_enter_tree() None[source]

Register this root’s isolated world with the tree so it gets stepped.

on_exit_tree() None[source]

Unregister + drop this root’s world (no leaked worlds on teardown).

Uses self._world (not the creating .world property) so exit never builds a world a never-used root never had. Re-entering the tree rebuilds a fresh world lazily and re-registers it; the registry guard makes the re-register idempotent.

strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property update_mode: simvx.core.descriptors.UpdateMode
property visible: bool
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
get_node_or_none(path: str) simvx.core.node.Node | None
find(target, *, direct: bool = False)
find_all(target, *, direct: bool = False)
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
on_ready() None
on_update(dt: float) None
on_fixed_update(dt: float) None
on_draw(renderer) None
on_picked(event: simvx.core.events.InputEvent) None
on_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()
call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None
property app
property tree: simvx.core.scene_tree.SceneTree
property physics
property physics_2d
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
class simvx.core.physics.root.PhysicsRoot2D(name: str = '', *, gravity: simvx.core.math.Vec2 = _DEFAULT_GRAVITY_2D, backend: str | None = None, factory: simvx.core.physics.root.WorldFactory2D | None = None, **kwargs: object)[source]

Bases: simvx.core.physics.root.PhysicsRoot

A node that owns an isolated :class:Physics2DWorld for its sub-branch.

The 2D sibling of :class:PhysicsRoot (recommended over a dims flag, per the design: it matches the taxonomy split and the resolution walk is a plain isinstance test). 2D bodies under a PhysicsRoot2D (with no nearer PhysicsRoot2D) simulate in this node’s 2D world, isolated from the tree’s default 2D world. Resolve a node’s 2D world with :func:resolve_world_2d.

It deliberately does NOT reuse the 3D base’s _world slot / world property (those build a 3D BuiltinPhysics): a PhysicsRoot2D owns its own 2D world via :attr:world_2d, built lazily by the 2D factory (defaults to BuiltinPhysics2D), and registers THAT world with the tree.

Initialization

Initialise the 2D physics root.

Args: name: Node name (defaults to the class name). gravity: Gravity (Vec2) for this root’s isolated 2D world. backend: Explicit backend-name override (arm 1 of the selection precedence; see :class:PhysicsRoot). None defers to the project setting / auto / Builtin arms. factory: Direct 2D backend-factory escape hatch; when given it bypasses the backend precedence and is used verbatim (test injection). Defaults to None -> resolve via backend precedence. **kwargs: Forwarded to :class:~simvx.core.node.Node.

property world_2d: simvx.core.physics.world2d.Physics2DWorld[source]

This root’s isolated 2D world, created lazily on first access.

Built from the explicit factory if one was given, else from the backend resolved by precedence (explicit backend > project setting > auto-discovered native > Builtin).

on_enter_tree() None[source]

Register this root’s isolated 2D world with the tree so it gets stepped.

on_exit_tree() None[source]

Unregister + drop this root’s 2D world (no leaked worlds on teardown).

Uses self._world_2d_obj (not the creating .world_2d property) so exit never builds a world a never-used root never had. Re-entering rebuilds a fresh world lazily and re-registers it (the registry guard is idempotent).

property world: simvx.core.physics.world.PhysicsWorld
strict_errors: ClassVar[bool]

True

script_error_raised

‘Signal(…)’

classmethod __init_subclass__(**kwargs)
property name: str
property update_mode: simvx.core.descriptors.UpdateMode
property visible: bool
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
get_node_or_none(path: str) simvx.core.node.Node | None
find(target, *, direct: bool = False)
find_all(target, *, direct: bool = False)
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
on_ready() None
on_update(dt: float) None
on_fixed_update(dt: float) None
on_draw(renderer) None
on_picked(event: simvx.core.events.InputEvent) None
on_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()
call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None
property app
property tree: simvx.core.scene_tree.SceneTree
property physics
property physics_2d
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()
simvx.core.physics.root.__all__

[‘PhysicsRoot’, ‘PhysicsRoot2D’, ‘resolve_world’, ‘resolve_world_2d’, ‘WorldFactory’, ‘WorldFactory2…