simvx.core.node¶
Node: Base node class with tree hierarchy, groups, and coroutine support.
Module Contents¶
Classes¶
Functions¶
Be told each node the frame walk visits, in which phase, and its cost in ms. |
Data¶
API¶
- simvx.core.node.log¶
‘getLogger(…)’
- simvx.core.node.observe_node_walk(observer: collections.abc.Callable[[Node, str, float], None] | None) None[source]¶
Be told each node the frame walk visits, in which phase, and its cost in ms.
observerreceives(node, phase, ms)withphaseeither"process"or"physics", once per node per phase, measured around that node’s own hooks and not its children’s. PassNoneto stop.There is one observer for the process, because there is one frame walk. A profiler is the caller this exists for: it means nothing has to re-implement the walk to time it, and a walk of one’s own silently does less than the engine’s.
- simvx.core.node.T¶
‘TypeVar(…)’
- simvx.core.node.D¶
‘TypeVar(…)’
- class simvx.core.node.Node(name: str = '', **kwargs)[source]¶
Base node with tree hierarchy, groups, and coroutine support.
Attributes: name: Unique name within the parent’s children. Defaults to the class name. parent: The parent
Node, orNoneif this is the root. children: Ordered collection of child nodes, accessible by name or index. visible: Whether this node (and its descendants) should be drawn. update_mode: Controls processing behaviour during pause (INHERIT,PAUSABLE,PAUSED_ONLY,ALWAYS,DISABLED). unique_name: WhenTrue, the node is registered in the tree for fast lookup viaSceneTree.get_unique_node().Example::
root = Node(name="Root") child = Node(name="Child") root.add_child(child) assert child.parent is root assert root.children["Child"] is child
Initialization
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- dev_warnings: ClassVar[bool]¶
None
- freeze_check_interval: ClassVar[float]¶
1.0
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- focus_mode¶
‘Property(…)’
- drag_threshold¶
‘Property(…)’
- focus_entered¶
‘Signal(…)’
- focus_exited¶
‘Signal(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- property name: str[source]¶
This node’s name, which is never empty.
The empty string is not a name a node can hold: it cannot be found by
- Meth:
find, it cannot appear in a node path, and a scene that saved one would load back a node named after its type. Assigning it raises; pass nonameto the constructor to get the type name instead.
- property visible_in_tree: bool[source]¶
Whether this node is effectively visible: its own flag AND every ancestor’s.
visibleanswers only for this node, so a visible node under a hidden parent still reportsTruethere. This is the answer the draw walk uses, and it is the one to test before skipping work for a hidden subtree.Maintained on every visibility change and every reparent, so reading it is a cached lookup rather than a walk up the parents. A node with no parent reports its own
visible.
- property input_player: simvx.core.input.players.InputPlayer[source]¶
Whose input this subtree answers to.
Reading it gives the player this node belongs to, inherited from the nearest ancestor that declared one and player 0 where nobody did. Assigning one declares it here and for everything below::
two = Input.add_player() two.pair(Input.controllers[1]) player_two_ship.input_player = two
Inside that subtree
Input.pressed("thrust")answers for player two with no argument, and an@on_input("jump")handler on one of its nodes hears only player two’s edge. That is what keeps local co-op from being a rewrite: a game gains the dimension by declaring it once rather than threading an id through every call site, and a single-player game never sees it because one implicit player owns every device.The scoping covers this subtree’s per-frame code (
on_update,on_fixed_update) and the input handlers on its nodes. Elsewhere, and everywhere in a game that declared nothing,Inputanswers for the default player.Assign
Noneto stop declaring one here and inherit again.
- property input_context: simvx.core.input.contexts.InputContext | None[source]¶
The bindings this node puts on top of the stack while it can hear input.
A context is how a game says “while this is on screen, these are the controls”: a vehicle whose
movesteers rather than walks, a pause menu that takes every name below it, a minigame with its own verbs::class Game(Node2D): input_context = {"move": [Axis2D.WASD, Controller.LEFT_STICK], "jump": [Key.SPACE]} class PauseMenu(Control): input_context = InputContext({"resume": [Key.ESCAPE]}, hides=InputContext.ALL)A plain dict is a context of its own entries;
InputContextis the spelling with a name orhides. Any node may declare one: the root’s covers the whole tree, a branch’s is in force while the branch is, a reparented node carries its own with it, and a scene that replaces the root takes the old root’s away.Declared on the class, as above, or assigned at any point; either way it is active exactly while this node can receive input, so no exception, early return or scene swap can leave one behind, and the switch that stops the node processing takes its context down too: a paused vehicle stops steering and gives
moveback to whatever declared it below. Contexts stack in activation order, later on top, and shadow by action name: the vehicle’smovesteers while the walking controller’sjumpstill jumps.hides=InputContext.ALLis the stronger form and takes every name below, which is what a pause menu wants. A menu whoseupdate_modeisPAUSED_ONLYtherefore hides nothing until the game is actually paused.Assigning
Nonetakes this node’s context off the stack.Changing the stack releases the actions whose bindings changed, with a real release edge, and presses nothing: a key held as the context changes reads unpressed until the player presses it again.
- add_child(node: simvx.core.node.T) simvx.core.node.T[source]¶
Add a node as a child, reparenting it if already in a tree.
Returns
nodeitself, at the type it was passed, so theself.hero = self.add_child(Sprite2D(...))the tutorials teach keeps the child’s own type rather than widening it toNode.Args: node: The node to add. Removed from its current parent first.
Raises: ValueError:
nodeisselfor one ofself’s ancestors – either would create a cycle in the scene tree – or :meth:destroyhas been called onnode, which is final.
- remove_child(node: simvx.core.node.Node) None[source]¶
Remove a child node from this node’s children.
Immediate:
nodehas left the tree by the time this returns, so itson_exit_treehas already run. Prefer :meth:destroyfrom inside a signal handler or a lifecycle hook, which runs the same teardown at the end of the frame instead of in the middle of a dispatch.
- reparent(new_parent: simvx.core.node.Node)[source]¶
Remove from current parent and add to new_parent.
- node_at(path, default=_NO_DEFAULT)[source]¶
Navigate the tree by path:
'Child/GrandChild'or'../Sibling'.A leading
/starts from the tree root, which the path may name as its first segment ('/Root/Player').defaultworks as it does on :func:getattr: without one, a path that names no such child raises :class:NodeNotFound; with one, that miss returns the default instead, sonode_at("HUD/Minimap", None)is how an optional node is read. A malformed path –'..'past the root – is a bug in the caller rather than an absent node, and raises either way.
- find(target, *, direct: bool = False)[source]¶
First descendant matching
target, orNone.targetmay be:a :class:
Nodesubclass: matches the firstisinstancedescendant. The result is typed as that subclass (find(Player) -> Player | None), so no cast is needed.a
str: matches the first descendant whosenameequals it.a predicate
(Node) -> bool: matches the first descendant it accepts.
Search is depth-first, pre-order, and recursive by default. Pass
direct=Trueto consider only this node’s direct children.
- find_all(target, *, direct: bool = False)[source]¶
All descendants matching
target(same matcher rules as :meth:find), in depth-first pre-order. Recursive by default;direct=Truelimits the search to direct children. Returns[]when nothing matches.
- expect(target, *, direct: bool = False)[source]¶
- Meth:
find, but a miss is an error rather than aNone.
Same matcher rules and the same typed result. Use it wherever the scene is expected to contain the node and a missing one is a bug in the scene: it fails at the lookup, naming what was asked for, instead of handing back a
Nonethat raises somewhere later with no clue why.Raises: NodeNotFound: nothing under this node matches
target.
- ancestor(target)[source]¶
Nearest ANCESTOR matching
target, orNone.The upward counterpart of :meth:
find, with the same matcher rules and the same typed result, walking parents from this node outwards.selfis never a candidate. This is how a node reaches the container it lives under (self.ancestor(Inventory)) without hard-coding how deep it sits.
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node][source]¶
Iterate this node and all descendants in DFS pre-order.
- property is_scene_root: bool[source]¶
Whether this node is the top of its scene.
True for a node with no parent, and for the current scene root of a
- Class:
~simvx.core.scene_tree.SceneTree, whose parent is the tree’s own top node rather than another scene node. This is the test to make before offering to delete, duplicate or reparent a node: a scene root can do none of the three.
- add_to_group(group: str)[source]¶
Add this node to a named group.
The tree lists a group’s members in join order: a node in the tree joins last now, and one outside it joins when it enters. Adding a group the node already has keeps its place. A node that has called :meth:
destroyrecords the group but joins no member list.
- on_ready() None[source]¶
Called once, ever, after the node and all its children enter the tree.
Override to perform initialisation that requires the scene tree – finding sibling nodes, connecting signals, spawning children. The
treeproperty is available. Called afteron_enter_tree()and after all children’son_ready().Decorate other methods with
@on_readyto register additional ready handlers; they fire after the override in declaration order.Once per node instance: a node that leaves the tree and comes back does not ready again, so the children spawned here are built once rather than once per entry, and the signals connected here are connected once rather than stacking up a duplicate per entry.
Which of the two entry hooks::
on_enter_tree() every entry, for as long as the node keeps coming back -- per-stay setup on_ready() once per node instance, ever -- one-time setupThe mirror rule follows from that: whatever
on_exit_tree()undoes has to be redone inon_enter_tree(). Putting it here leaves the node dead the second time it joins a tree.Example::
def on_ready(self): self.sprite = self.node_at("Sprite") self.health_changed.connect(self._update_hud)
- on_enter_tree() None[source]¶
Called on every entry into the scene tree, before
on_ready().Override for the setup that belongs to each stay in the tree: claiming a slot on the tree, subscribing to a tree signal, restarting whatever
on_exit_tree()stopped. Unlikeon_ready(), which fires once per node instance and never again, this fires as often as the node is added.The whole incoming subtree is bound to the tree before any of its entry hooks run, so a hook may look up nodes the walk has not reached yet – by group, by unique name or by path – and every one of them already answers
node.tree. Their ownon_enter_treemay not have run yet, though, so read their state rather than depending on their setup;on_ready()is where the subtree is fully built.Example::
def on_enter_tree(self): self.add_to_group("enemies") self.tree.screen_resized.connect(self._relayout)
- on_exit_tree() None[source]¶
Called when the node is about to leave the scene tree.
Override to clean up resources, disconnect external signals, or persist state. Children have already exited by the time this fires on the parent.
Example::
def on_exit_tree(self): self.save_progress() self.remove_from_group("enemies")
- on_update(dt: float) None[source]¶
Called every frame for game logic.
Args: dt: Seconds elapsed since the previous frame (variable timestep).
Override for movement, AI, animation triggers, or any per-frame update. Obeys
update_mode– disabled or paused nodes are skipped automatically.Decorate other methods with
@on_updateto register additional per-frame handlers; they fire after the override in declaration order. For state held while a button is pressed, pollInput.pressed("name")from insideon_update.Example::
def on_update(self, dt): self.position += self.velocity * dt
- on_fixed_update(dt: float) None[source]¶
Called at a fixed timestep (default 60 Hz) for physics logic.
Args: dt: Fixed time step in seconds (e.g. 1/60).
Override for deterministic physics updates – forces, collision responses, rigid-body integration. Runs independently of the render frame rate.
Example::
def on_fixed_update(self, dt): self.velocity += self.gravity * dt self.move_and_slide(dt)
- on_draw(canvas) None[source]¶
Called each frame for custom 2D drawing.
Args: canvas: The 2D drawing surface this frame is recorded into.
Override to issue immediate-mode draw calls:
rect,circle,line,polygon,text,textureandnine_patch, each painted with afillcolour, astrokecolour, or both. Called only whenvisibleisTrue.The 2D renderer is retained (“build once”): output is re-collected only when a
Propertychanges. Ifon_drawreads non-Property state (a plain attribute updated by a signal or timer,tree.nowanimation), call :meth:queue_redrawwhen that state changes so the new frame is collected. This is identical on live and headless: a body that mutates withoutqueue_redrawfreezes on both.Drawing happens in the node’s own space:
(0, 0)is the node’s origin, and its position, rotation and scale, and every ancestor’s, apply to what it draws.Example::
def on_draw(self, canvas): canvas.circle((0, 0), 10, fill=(1, 0, 0, 1))
- on_draw_3d(gizmos) None[source]¶
Called each frame for custom immediate-mode 3D drawing.
Args: gizmos: The active 3D draw surface, which issues primitives that render inside the 3D scene pass.
Override to issue immediate 3D primitives positioned in world space, as distinct from the retained mesh pipeline that a
MeshInstance3Dfeeds. Called once per frame while the node is visible, and never for a scene with no 3D camera.This is not :meth:
on_draw, which draws in 2D and receives an entirely different surface. A node may implement both.Example::
def on_draw_3d(self, gizmos): gizmos.draw_text_3d(text=self.label, position=self.world_position)
- on_picked(event: simvx.core.input.events.PickEvent) bool | None[source]¶
Called when a 3D mouse-pick event hits this node’s collision shape.
Args: event: The pick, carrying the camera ray, the button, and where along the ray the hit landed.
Returns: True to claim the press, which stops it there: no action state, no edge and no broadcast behind it. False or None lets it through, so clicking a barrel can mean both touching it and firing at it, which is usually what a game wants. Anything else raises naming the handler, the rule every input handler answers to.
Override to react to direct interaction with this 3D object – selection, dragging, context menus. The override hears a press’s pick alone; a release’s pick reaches only an
@on_pickedhandler that asks for it withedge="release"or"both".Example::
def on_picked(self, event): if event.button in Pointer.PRIMARY: self.selected = TrueThe decorator form, :func:
~simvx.core.decorators.on_picked, routes by button, so one method per button replaces the test::@on_picked(Pointer.PRIMARY) def select(self, event): self.selected = True @on_picked(Mouse.RIGHT) def open_menu(self, event): ...Decorated handlers run first, in declaration order, and this override runs after them only if none claimed.
- on_gui_input(event: simvx.core.input.events.GuiInputEvent) bool | None[source]¶
Override to handle input aimed at this node in particular.
This is the targeted route rather than the broadcast, and the 2D half of the phase :meth:
on_pickedserves in 3D. Keys and text go to the focus owner; a pointer goes to the node holding it through- Meth:
capture_pointer, else along the chain of controls under it; the wheel goes along that chain. ReturnTrueto claim the event, which stops it reaching whatever is behind or above and suppresses the router’s own fallbacks (focus traversal, Escape dismissing an overlay). ReturnFalseor nothing to let it carry on. That is the same answer an@on_inputhandler gives, and anything else raises naming the handler.
A hit test only ever finds a :class:
~simvx.core.ui.Control, so a plain node reaches this by holding a capture. It hears every event the phase delivers, a held key’s auto-repeats included (each withevent.echoset), which is the feed a text field reads.The event is one of four types and a handler tells them apart with
isinstance, in the branch that reads the field rather than above several of them: a guard placed above is only correct while every branch under it returns, and the next edit moves one.The decorator form, :func:
~simvx.core.decorators.on_gui_input, does the telling apart for you, with@on_input’s filters::@on_gui_input(ScrollEvent) def scrolled(self, event): self.offset -= event.scroll[1] * self.step return True @on_gui_input(Pointer.PRIMARY, edge="release") def released(self, event): ...Decorated handlers run first, in declaration order, and this override is the catch-all after them: it runs only if none claimed.
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle[source]¶
Register a generator coroutine to run each frame. Returns a cancellable handle.
- stop_coroutine(gen_or_handle)[source]¶
Stop and remove a running coroutine (accepts generator or CoroutineHandle).
- property focused: bool[source]¶
Whether this node holds keyboard focus. Read-only: :meth:
grab_focusand :meth:release_focusmove it.
- property focus_rect: tuple[float, float, float, float] | None[source]¶
Where this node is on screen,
(x, y, width, height), as directional focus reads it.None for a node with no extent on screen, which directional focus passes over. A control answers its rect; a node that should be a directional-focus destination and is not a control overrides this.
- drag_threshold_for(pointer_type: simvx.core.input.enums.PointerType | str) float[source]¶
The drag threshold in force here for pointer_type: this node’s, an ancestor’s, or the tree’s.
- grab_focus() None[source]¶
Take keyboard focus, which
tree.focus_ownerthen names. Outside a tree this does nothing.Raises: ValueError:
focus_modeisNONE. A widget that focuses itself declares the mode it takes focus in,FocusMode.CLICKwhen it is not a Tab stop.
- capture_pointer(pointer_id: int | None = None) None[source]¶
Route every event from pointer_id to this node until it is released.
A drag needs the pointer past the edge of whatever started it: a slider thumb the cursor has run off, a splitter, a canvas being panned. While the capture is held the hit test is skipped for that pointer and the events go straight here.
Args: pointer_id: Which pointer. 0 is the mouse; every other id is the opaque one its events carry. Left unset, the pointer of the event being handled, so a slider dragged by a finger holds that finger; outside a handler, the mouse.
Capture belongs to the node rather than to the control: a 3D gizmo and a world-space handle drag exactly as a widget does. It lasts until
- Meth:
release_pointer, until the node leaves the tree, or until the window loses focus, because a node the tree no longer holds receives nothing and an interrupted press was never completed.
- release_pointer(pointer_id: int | None = None) None[source]¶
Give pointer_id back. A no-op when this node is not what holds it.
Left unset, the pointer of the event being handled, as
- Meth:
capture_pointer.
- queue_redraw() None[source]¶
Mark this node’s
on_drawoutput stale (re-capture it next frame).The manual escape hatch for an
on_drawbody that reads non-Property state and changes ONCE (a signal/timer poke). For per-frame animation set- Attr:
dynamicinstead. Idempotent and cheap (a no-op once already dirty).
Drawable2D(everyNode2D/Control/CanvasLayer) also gets this called automatically by the blanketProperty.__set__hook on any changed Property, so drawing from Property state never needs it. A plainNodeHUD/menu (_render_auto_dirtyisFalse) calls it by hand.
- property render_dirty: bool[source]¶
Whether
on_drawoutput changed since the last upload (introspection).
- destroy()[source]¶
Schedule this node for removal at the end of the current frame.
This is the engine’s deferred removal, and the only spelling of it: a node marked here stays alive, in the tree, for the rest of the frame, and its subtree is carried out in one piece at the end-of-frame sync point. That is what makes it safe to call from a signal handler, a collision callback or a lifecycle hook, where tearing a node down on the spot would mutate a structure the caller is still walking.
- Meth:
remove_childis the immediate counterpart, for the cases that genuinely need the node gone before the call returns (reparenting).
Removal does not cost a node the events it was still owed, and that is true of
remove_childtoo: the physics seam reports the contacts a destroyed body was in on the step after it goes, andseparated/body_exitedcarry that node. The seam holds it for that step itself, so the naming does not depend on which removal path was taken.Always final. A node that is not in a tree has no frame to defer to, so it is finished on the spot rather than ignoring the call; one that is in a tree is queued for the end of the frame. Either way the node is spent afterwards, and :meth:
add_childrefuses to put it back in a tree.Idempotent: a second call does nothing, before or after the removal has run, so the same enemy can be destroyed by two handlers in one frame.
- Attr:
destroyingreports the pending state.
The whole subtree is finished with the node, as it is removed with it: every descendant still attached when the removal runs is spent too. A child meant to outlive its parent is moved out before then.
The node leaves its groups on the call, so
tree.groupstops listing it at once; its descendants leave theirs when the removal runs.Signal connections made through this node’s bound methods,
EventBussubscriptions among them, are disconnected on the call, so emitters stop dispatching to it on the next emit; its descendants’ connections end when the removal runs. Lazy weak-ref cleanup inSignal.emitcovers nodes that are collected withoutdestroy().
- property destroying: bool[source]¶
Whether :meth:
destroyhas been called and the removal has not run yet.True from the
destroy()call until the end-of-frame drain carries the node out of the tree, and False again afterwards (the node object itself stays valid; Python frees it when the last reference goes). The call drops the node from its groups, so group queries already leave it out; use this for a reference held some other way::if self.target is not None and self.target.destroying: self.target = NonePer node, not per subtree: a child of a destroying node reports False until it is itself queued. Only nodes queued while in a tree are ever marked: a detached node has no frame to defer to, so
destroy()on one finishes it immediately and this never turns True for it.
- property destroyed: bool[source]¶
Whether this node has been spent: :meth:
destroyhas run its course.True once the removal has carried the node and its subtree out of the tree (at once for a node destroyed while detached), and never False again: a spent node cannot be added back to a tree. Something holding a reference to the node, such as a signal observer, uses this to know the node’s lifetime is over.
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None[source]¶
Escape hatch: run
method(*args)at the end of this frame, outside tree traversal, instead of now.Discouraged: prefer a safe-by-default path when one exists. SimVX already makes the common cases safe without deferring: the process loop and signal dispatch iterate snapshots (so adding/removing nodes mid-loop does not corrupt iteration), :meth:
destroyis already a deferred delete,Property(coalesce=True)collapses repeated writes, andtree.events.publish_deferred(...)decouples event delivery. Reach forcall_deferredonly when you must mutate from a context none of those cover, and document why at the call site.methodis a bound method or any callable (type-safe; never a string method name). Calls run once, in queue order, at the end-of-frame sync point; anything queued during that drain runs on the next frame. A call bound to this node is dropped if the node has left the tree by the time the queue drains, and runs through :meth:_safe_callso a failure obeys the same strict/release policy as any other lifecycle hook.
- property tree: simvx.core.scene_tree.SceneTree[source]¶
The SceneTree this node belongs to.
- property viewport_size: simvx.core.math.types.Vec2[source]¶
The size of the render target this node draws into, right now.
What a node needs to lay anything out. Read it where it is used and compute from it; do not copy it into a field, because a window resize, a fullscreen toggle and a phone rotating all move it. An
on_drawthat read it is re-captured when it changes, with noqueue_redrawand nodynamic; code outsideon_drawthat lays out from it listens totree.screen_resized::def on_draw(self, renderer): width, height = self.viewport_size renderer.rect((0, height * 0.7), (width, height * 0.3), fill=UI_BG)In the pixels of the space you draw in. Engine units are logical pixels, and the answer is measured in the units this node draws in. A world node gets screen pixels. A
Controlunder the tree’sui_scalegets UI pixels: at 1.5 a 1200-pixel-wide window is 800 of them, because a 200-pixel button is still 200 pixels to the control and fewer fit, which is browser zoom’s model. :attr:viewport_insetsfollows the same rule.Which target. A :class:
~simvx.core.viewport.SubViewportrenders its children into its own offscreen texture, so a node under one gets that texture’s size rather than the window’s. Everything else gets the tree’sscreen_size, aRenderView’s children included: a RenderView captures the main world from a second camera and its children are ordinary main-scene content, so the target they draw into is the screen. Asked of a SubViewport itself the answer is its own target, the same number as itstexture_size, which is what lets a SubViewport lay its own children out without naming its size twice.No default. Off-tree and outside any SubViewport this raises rather than answering with a window size nobody asked for: a fallback would be a literal that is right on one machine, wrong on the next, and invisible until someone runs the game on a phone. A node is in a tree from
on_enter_treeonward, which is before every hook that draws or lays out.Raises: RuntimeError: when the node is in no tree and under no SubViewport.
- property viewport_insets: tuple[float, float, float, float][source]¶
How much of each edge of this node’s render target is covered, as
(left, top, right, bottom).A soft keyboard sliding up is a bottom inset, a notch a top or side one, a home indicator a small bottom one. Measured inward from the edges of :attr:
viewport_sizeand in the same units, the pixels of the space this node draws in, so a control can use them as margins directly and a world node can compare them with its own coordinates::def on_update(self, dt): left, top, right, bottom = self._panel.viewport_insets self._panel.margin_left = left + PAD self._panel.margin_bottom = -(bottom + PAD)Read where it is used rather than remembered: a keyboard opens and a phone rotates. An
on_drawthat read it is re-captured when it changes; code outsideon_drawlistens totree.screen_insets_changed. The screen’s own insets, in screen pixels, aretree.screen_insets, which is what a backend writes. A node under aSubViewportdraws into an offscreen texture that nothing on the glass covers, so its insets are zero.Raises: RuntimeError: when the node is in no tree and under no SubViewport.
- property physics[source]¶
Spatial-query accessor bound to this node’s physics world, or
None.Mirrors :attr:
app/ :attr:tree: available once in-tree. Returns aPhysicsQueryscoped to the same world the node’s body lives in (resolved via the nearestPhysicsRootancestor, else the tree default), exposingraycast/raycast_all/shapecast/overlapwith typed results andmask/excludefilters. Built fresh per access (not cached): the resolved world can change across re-parent / change_scene, and the wrapper is a thin two-reference object on the cold query path. Bind it locally if a hot loop wants to reuse it.
- property physics_2d[source]¶
2D spatial-query accessor bound to this node’s 2D physics world, or
None.The 2D sibling of :attr:
physics: available once in-tree, returns aPhysicsQuery2Dscoped to the same 2D world the node’s body lives in (resolved via the nearestPhysicsRoot2Dancestor, else the tree’s 2D default), exposingraycast/raycast_all/shapecast/overlapwith typed 2D results andmask/excludefilters. Built fresh per access (cold query path).
- classmethod get_properties() dict[str, simvx.core.descriptors.Property][source]¶
Return all Property descriptors declared on this node class and its bases.
- class simvx.core.node.Timer(**kwargs)[source]¶
Bases:
simvx.core.node.NodeNode that emits :attr:
timeoutafterdurationseconds.A Timer counts down in
on_update, so it runs on frame time, not on the fixed-step physics clock, and it obeysupdate_mode: aPAUSABLETimer stops counting while the tree is paused. Nothing happens until- Meth:
startis called orautostartis set at construction.
With
one_shot(the default) the timer fires once and stops. Withone_shot=Falseit repeats, and the leftover time from the frame that crossed zero is carried into the next cycle, so a repeating timer does not drift at low frame rates.Connect to it like any other signal::
timer = Timer(duration=0.5, one_shot=False, autostart=True) timer.timeout.connect(self.spawn_enemy) self.add_child(timer)
- Attr:
time_leftreports the seconds remaining and :attr:stoppedwhether the countdown is idle; :meth:stopresets both without emitting.
Initialization
- duration¶
‘Property(…)’
- one_shot¶
‘Property(…)’
- autostart¶
‘Property(…)’
- timeout¶
‘Signal(…)’
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- dev_warnings: ClassVar[bool]¶
None
- freeze_check_interval: ClassVar[float]¶
1.0
- dynamic: bool¶
False
- visible¶
‘Property(…)’
- update_mode¶
‘Property(…)’
- focus_mode¶
‘Property(…)’
- drag_threshold¶
‘Property(…)’
- focus_entered¶
‘Signal(…)’
- focus_exited¶
‘Signal(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- property input_player: simvx.core.input.players.InputPlayer¶
- property input_context: simvx.core.input.contexts.InputContext | None¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_enter_tree() None¶
- on_exit_tree() None¶
- on_fixed_update(dt: float) None¶
- on_draw(canvas) None¶
- on_draw_3d(gizmos) None¶
- on_picked(event: simvx.core.input.events.PickEvent) bool | None¶
- on_gui_input(event: simvx.core.input.events.GuiInputEvent) bool | None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- property focused: bool¶
- property focus_rect: tuple[float, float, float, float] | None¶
- drag_threshold_for(pointer_type: simvx.core.input.enums.PointerType | str) float¶
- grab_focus() None¶
- release_focus() None¶
- capture_pointer(pointer_id: int | None = None) None¶
- release_pointer(pointer_id: int | None = None) None¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- property destroyed: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property viewport_size: simvx.core.math.types.Vec2¶
- property viewport_insets: tuple[float, float, float, float]¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶