# Node System Nodes are the building blocks of every SimVX game. They form a tree hierarchy managed by the `SceneTree`. ## Lifecycle Every node goes through these stages: 1. **Construction** -- `__init__()` sets up properties 2. **Enter tree** -- Node is added to the scene tree via `add_child()` 3. **Ready** -- `ready()` is called once, after all children are ready (bottom-up) 4. **Process** -- `process(dt)` is called every frame 5. **Physics process** -- `physics_process(dt)` is called at fixed intervals (default 60 Hz) 6. **Exit tree** -- Node is removed from the tree via `destroy()` ```python class Enemy(CharacterBody3D): health = Property(100, range=(0, 200)) def ready(self): self.health = 100 def process(self, dt): self.rotate((0, 1, 0), 45 * dt) # degrees def physics_process(self, dt): self.move_and_slide(dt) ``` ## Properties `Property` declares editor-visible, serializable values with optional validation: ```python from simvx.core import Node3D, Property class Tank(Node3D): speed = Property(5.0, range=(0, 20), hint="Movement speed") armor = Property("heavy", enum=["light", "medium", "heavy"]) active = Property(True) ``` Query all properties on a class with `get_properties()`: ```python Tank.get_properties() # {"speed": Property(...), "armor": Property(...), "active": Property(...)} ``` ## Hierarchy Nodes have a single parent and any number of children: ```python root = Node(name="Root") player = Node3D(name="Player") camera = Camera3D(name="Camera", position=(0, 5, 10)) root.add_child(player) player.add_child(camera) # Access children by name, index, or attribute root.children["Player"] # by name root.children[0] # by index root.children.Player # by attribute # Path-based access via __getitem__ root["Player/Camera"] # slash-separated path camera.get_node("../Player") # relative path (sibling) camera.get_node("/Root") # absolute path # Node properties camera.parent # direct parent camera.path # "/Root/Player/Camera" camera.tree # the SceneTree ``` ## Signals Signals provide decoupled event communication. `connect()` returns a `Connection` handle for later disconnection: ```python from simvx.core import Signal class HealthComponent(Node): def __init__(self): super().__init__() self.died = Signal() self.hp = 100 def take_damage(self, amount): self.hp -= amount if self.hp <= 0: self.died.emit() # Connect and get a Connection handle health = HealthComponent() conn = health.died.connect(lambda: print("Game over")) # One-shot connection: auto-disconnects after first call health.died.connect(on_first_death, once=True) # Disconnect manually conn.disconnect() ``` ## Groups Tag nodes for batch operations: ```python enemy.add_to_group("enemies") enemy.is_in_group("enemies") # True # Get all nodes in a group all_enemies = scene_tree.get_group("enemies") ``` ## Coroutines Generator-based coroutines for sequential async logic: ```python from simvx.core import wait, parallel, tween def cutscene(self): yield from wait(1.0) # pause 1 second yield from tween(cam, "position", target, 2.0) # animate yield from wait(0.5) self.start_dialog() # Run multiple animations simultaneously self.start_coroutine(parallel( tween(a, "position", pos_a, 1.0), tween(b, "position", pos_b, 1.0), )) ``` ## Destroying Nodes Remove nodes safely at the end of the frame: ```python enemy.destroy() ``` ## Finding Nodes ```python # Find first child of type camera = root.find(Camera3D) # Find all descendants of type all_lights = root.find_all(Light3D) all_meshes = root.find_all(MeshInstance3D, recursive=True) ``` ## API Reference See {py:mod}`simvx.core.engine` for the complete Node API.