How To Use Nodes

Use concrete node types inside with_node! and with_node_mut! closures, and use SceneNode helper macros for hierarchy metadata operations.

Typed access signatures

with_node_mut!(ctx.run, NodeType, node_id, |node| -> V { ... }) -> Option<V>with_node!(ctx.run, NodeType, node_id, |node| -> V { ... }) -> Vwith_base_node_mut!(ctx.run, BaseType, node_id, |node| -> V { ... }) -> Option<V>with_base_node!(ctx.run, BaseType, node_id, |node| -> V { ... }) -> V

NodeType accepts any concrete node type from Nodes docs when using with_node!/with_node_mut!.

BaseType accepts ancestor types for inheritance-aware access with with_base_node!/with_base_node_mut!.

Exact type mismatch on with_node* fails typed access and mutable form returns None.

Use base access when query returns mixed descendants under a shared base node type.

Mutate Concrete Node Data

The typed nodes in the hierarchy are what you pass to runtime access closures. Use with_node! to return field data or tuples from read-only access:

let texture = with_node!(ctx.run, Sprite2D, node_id, |sprite| {    sprite.texture}); let (position, visible) = with_node!(ctx.run, Sprite2D, node_id, |sprite| {    (sprite.position, sprite.visible)});

Create node at runtime and initialize fields:

// create and attach sprite to parentlet sprite_id = create_node!(ctx.run, Sprite2D, "EnemySprite", ["enemy"], parent_id);let texture = texture_load!(ctx.res, "res://texture.png");with_node_mut!(ctx.run, Sprite2D, sprite_id, |s| {  s.texture = texture;  s.position = Vector2::new(120.0, 48.0);});

Mutable closures can return computed values too.

let new_hp = with_state_mut!(ctx.run, EnemyState, enemy_id, |state| {    state.health -= 10;    state.health}); let new_x = with_node_mut!(ctx.run, Node2D, node_id, |node| {    node.position.x += 8.0;    node.position.x});

Base type workflow for query results

Use base access when a query matches different concrete types that still share one base. This keeps one typed closure path.

let ids = query!(ctx.run, all(base[Node3D], tags["enemy"]));for id in ids {    let _ = with_base_node_mut!(ctx.run, Node3D, id, |node| {        node.position.y += 0.1;    });}

Deref Inheritance

Descendant nodes include a base field of their ancestor type and implement Deref/DerefMut. Take Sprite2D and Node2D for example:

pub struct Sprite2D {    pub base: Node2D,    pub texture: TextureID,} impl Deref for Sprite2D {    type Target = Node2D;    fn deref(&self) -> &Self::Target { &self.base }} impl DerefMut for Sprite2D {    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.base }}

Deref access is resolved at compile time, so there is no runtime overhead. When you have a Sprite2D, you treat it as also having Node2D fields like position and visibility, so those fields are used as if they are native on the concrete node even though they come through deref.

with_node_mut!(ctx.run, Sprite2D, node_id, |sprite| {    // Sprite2D field    sprite.texture = texture_load!(ctx.res, "res://texture.png");     // Node2D fields via Deref/DerefMut    sprite.position.x += 12.0;    sprite.visible = true;    sprite.z_index = 3;});

SceneNode Metadata Macros

For name/parent/children/reparent operations, use SceneNode helper macros:

let name = get_node_name!(ctx.run, ctx.id).unwrap_or_default();let parent_id = get_node_parent_id!(ctx.run, ctx.id).unwrap_or(NodeID::nil());let children = get_node_children_ids!(ctx.run, ctx.id).unwrap_or_default();set_node_name!(ctx.run, ctx.id, "Player");reparent!(ctx.run, new_parent_id, child_id);