Node
The base of every display object: transform, children, lifecycle hooks, input handlers and events.
ts
import { Node, Event, Align, PauseMode } from "willow";Node
ts
class NodeScene-graph base. Every visual is a Node. Nodes carry position plus visibility, can own children, and can be removed from their parent or disposed. Lifecycle hooks (onReady, onUpdate, onEnter, onExit) are assignable callable slots.
Node: constructors
ts
new Node(name?: string)Creates an invisible grouping node with no visual representation of its own. name is optional and purely for debugging/creation-site tooling. A plain new Node() is the building block every scene root and container is made from.
Node: properties
| Member | Description |
|---|---|
filters: Filter[] | List of visual filters applied to this node's rendered output. |
alpha: float | Per-node opacity multiplier in 0.0..1.0. Composes with parent alpha. |
interactable: boolean | Pointer-input enable switch. Defaults to true. Writing false takes the node out of hit-testing even while a pointer handler is assigned; writing true restores the handler-driven behaviour. It never makes a handler-less node hit-testable: a node still participates only while it has a pointer handler assigned. |
name: string | Debug / lookup name. Seeded from the constructor argument (empty when omitted); assigning a non-string raises a TypeError. |
userData: any | Free-form value the script attaches to this node. undefined until assigned. The runtime never reads it. |
blendMode: string | Compositor blend mode. One of "normal" | "add" | "multiply" | "screen" | "erase" | "mask" | "below" | "none". "add" brightens (additive particle FX); "mask" clips against this node's alpha. An unknown string raises a typed VM error. Defaults to "normal". |
globalOrder: int | Secondary render-sort key (default 0). Breaks ties within the same renderLayer: among nodes on one layer, a higher globalOrder draws on top, independent of tree/insertion order. renderLayer still dominates it. A plain settable int. |
renderLayer: int | Primary render-sort key in [0, 255]. A higher renderLayer draws on top of a lower one regardless of scene-tree position, the mechanism for UI-over-world or additive-FX-over-everything. Reserved conventional values live on engine.scene (LAYER_BACKGROUND, LAYER_DEFAULT, LAYER_UI, LAYER_OVERLAY). A value outside [0, 255] raises a typed VM error. Defaults to 0. A node with no layer of its own draws on its nearest ancestor's layer, so a subtree (including children added later) follows the layer set on its root. Reading returns that effective layer; writing makes the layer explicit for the node and its unset descendants; clearRenderLayer() goes back to inheriting. |
paused: boolean | Stops this subtree's onTick, onUpdate, onLateUpdate, ticker and tweens while true. A detached node is not walked at all, so reattaching resumes it. Default false. |
pauseMode: PauseMode | How this node treats a pause above it: a PauseMode constant. INHERIT (default) follows its parent, ALWAYS keeps running (a pause menu inside the paused tree), WHEN_PAUSED runs only while an ancestor is paused, DISABLED never runs. |
timeScale: float | Scales this subtree's time (default 1): onTick runs floor(accumulator) times per app tick, dt for onUpdate and onLateUpdate is multiplied, and the node ticker follows the tick count. Must be >= 0. |
unscaled: boolean | When true this subtree ignores App.timeScale and its ancestors' timeScale. Default false. |
interpolate: boolean | When true the renderer draws x, y, rotation, scaleX and scaleY between their values at the last two ticks, using App.tickAlpha: move the node in onTick and it still looks smooth at a frame rate above App.tps. Only the drawing is blended; x and the other fields always read the logical value. Call resetInterpolation() after a teleport, or the node slides across the screen for one tick. Default false. |
readonly ticker: Ticker | The node's own Ticker, made on first read. It ticks with the node, pauses and scales with it, and disposing the node rejects its pending waits with Cancelled. |
readonly parent: Node | This node's parent in the scene graph. undefined while detached. |
pivotX: float | Pivot x in local pixel space. The rotation/scale anchor. (0, 0) leaves rotation around the top-left. Centring (e.g. setAnchor(0.5, 0.5) or setPivot(w/2, h/2)) makes a node spin in place. |
pivotY: float | Pivot y in local pixel space. See pivotX. |
rotation: float | Local rotation in radians. |
scaleX: float | Local x scale factor. 1.0 is the authored size. |
scaleY: float | Local y scale factor. 1.0 is the authored size. |
skewX: float | Local x skew in radians. |
skewY: float | Local y skew in radians. |
visible: boolean | Whether this node (and its subtree) renders. |
x: float | Local x position in parent space. |
y: float | Local y position in parent space. |
zOrder: int | Render order within the parent. Higher draws on top. |
Node: callbacks
| Member | Description |
|---|---|
onPointerMove: Function | Fires for pointer movement over this node. |
onPointerEnter: Function | Fires directly when the pointer enters this node. Enter and leave do not bubble. A transition fires leave on the old node before enter on the new node. |
onPointerLeave: Function | Fires directly when the pointer leaves this node. |
onDragStart: Function | Fires once movement crosses the configured drag dead zone. |
onDrag: Function | Fires for movement after a drag has started. |
onDragEnd: Function | Fires when an active drag ends. |
onPinch: Function | Fires during a two-finger pinch/rotate gesture over this node. Registering this ALSO makes the node participate in hit-testing, same as onPointerDown et al. Called as onPinch(e: PinchContext): void. |
onWheel: Function | Fires when the mouse wheel or trackpad scrolls while the pointer is over this node (or over a descendant with no onWheel of its own). Registering this makes the node participate in hit-testing. Called as onWheel(e: WheelEvent): void. |
onEnter: Function | Fires when the node is added to a parent. Assign null to detach. Called as onEnter(): void. |
onExit: Function | Fires when the node is removed from its parent. Assign null to detach. Called as onExit(): void. |
onReady: Function | Fires once on the first frame this node is in the tree. Assign null to detach. Called as onReady(): void. |
onUpdate: Function | Fires every frame after onReady. Assign null to detach. Called as onUpdate(dt: float): void where dt is the frame-time in seconds. |
onTick: Function | Fires once per logic tick (App.tps per second), in the tick phase before the frame, parent before children. Visibility does not stop it; paused and a detached node do. Assign null to detach. A hook that throws is reported once and disabled. Called as onTick(): void. |
onLateUpdate: Function | Fires every frame after the whole tree's onUpdate and the tweens (cameras and followers). Assign null to detach. Called as onLateUpdate(dt: float): void. |
onPointerDown: Function | Fires when a pointer is pressed over this node. Assign null to detach. A node participates in hit-testing only while it has a pointer handler assigned. Called as onPointerDown(e: PointerEvent): void. |
onPointerUp: Function | Fires when a pointer is released over this node. Assign null to detach. Called as onPointerUp(e: PointerEvent): void. |
onClick: Function | Fires when a press and release both land on this node. Assign null to detach. Called as onClick(e: PointerEvent): void. |
Node: methods
| Member | Description |
|---|---|
setMask(mask: Node | null): void | Clips this node's rendered subtree to mask. The mask's own transform is ignored by the renderer. Child transforms still apply. Masks do not affect hit testing. Passing null clears it. |
clearMask(): void | Clears the subtree mask, if any. |
getMask(): Node | null | Returns the current subtree mask, or null when none is set. |
addFilter(filter: Filter): void | Appends a filter to this node's filter chain. |
removeFilter(filter: Filter): void | Removes a filter from this node's filter chain. |
clearFilters(): void | Removes all filters from this node. |
getFilters(): Filter[] | Returns this node's active filter chain. |
setFilters(filters: Filter[]): void | Replaces this node's filter chain with the given array of filters. |
setCustomPaint(paint: ((painter: Painter) => void) | null): void | Installs a function that draws this node itself each frame, instead of its normal content. The function receives a Painter; call painter.paintDefault() to keep the normal content. Passing null removes it. |
enablePhysics(config: PhysicsConfig): void | Turns this node into a physics root with its own isolated world. Nodes below it get bodies with setBody; the scene steps the world each logic tick. Raises if an ancestor or a descendant is already a physics root. |
disablePhysics(): void | Tears this physics root down: every body below it is removed. |
isPhysicsRoot(): boolean | True when this node is a physics root. |
setBody(definition: PhysicsBodyDef): void | Attaches a body at this node's current position and rotation, replacing any existing body. Raises when no ancestor is a physics root or the definition is invalid. |
removeBody(): void | Detaches this node's body; does nothing without one. |
getBody(): PhysicsBody | null | Returns this node's body, or null when it has none. |
setBodyEnabled(enabled: boolean): void | Takes the body out of, or back into, the simulation, keeping its state. |
bodyEnabled(): boolean | True when this node has a body that takes part in the simulation. |
stepPhysics(dt: float): void | Advances the physics root enclosing this node by dt seconds and skips that root's next automatic step. Raises when no ancestor is a physics root. |
setCacheAsTree(enabled: boolean, mode?: int): void | Enables or disables this node's subtree as a cached texture. In CacheTreeMode.MANUAL mode (default), call invalidateCache() when the subtree changes; in CacheTreeMode.AUTO mode, changes are detected automatically (slight overhead per change). Renders the whole subtree to an offscreen texture on the next frame and draws the texture thereafter until invalidated. Useful for reducing overdraw in complex, rarely-changing UI or tilesets. Requires a real renderer; has no effect in headless builds. |
invalidateCache(): void | Invalidates the cache for this node's subtree, forcing a re-render on the next frame. Has no effect if cache-as-tree is not enabled. Called automatically in CacheTreeMode.AUTO on any setter; required manually in CacheTreeMode.MANUAL mode. |
isCacheEnabled(): boolean | Returns true if this node's subtree is cached as a texture. |
resetInterpolation(): void | Forgets the previous tick: the next draw shows the node exactly where it is now, with no slide from where it was. Call it after setting x/y to teleport a node that has interpolate on. |
dispose(): void | Releases this node's native resources now rather than whenever the collector runs. Calling dispose() twice is a silent no-op. Every other method or field on a disposed node, on every subclass, raises a catchable InvalidStateError that names it, for example "InvalidStateError: Node.x: node has been disposed". A disposed node also leaves its parent: children() no longer lists it and parent.contains(deadChild) answers false. engine.root2d cannot be disposed. |
add(child: Node): void | Adds child to this node's child list and takes ownership. The scene graph holds a strong reference; removeChild drops it. |
addAt(child: Node, index: int): void | Adds child at child index index, so it draws before the children already at or after that index. index runs 0..children().length; re-adding an existing child of this node moves it, and its valid range is then 0..children().length, 1. An out-of-range index raises a RangeError. |
children(): Array<Node> | Returns a snapshot array of this node's direct children. |
contains(child: Node): boolean | True if child is a direct child of this node. |
directionTo(other: Node): Vec2 | Returns the unit vector pointing from this node toward other in world space. Returns the zero vector if both nodes share a position. |
distanceTo(other: Node): float | Euclidean distance between this node's world position and other's, in pixels. |
localToWorld(p: any): Vec2 | Maps a point in this node's local space to world space. Returns a freshly-allocated Vec2. p accepts any { x, y }-shaped value (a Vec2 or a plain object literal), only the numeric x/y fields are read. |
toScreen(x: float, y: float): Vec2 | Maps a point in this node's local space to screen units (engine.screen2d's space, the framebuffer in the units the camera viewport is given in). A node under the world root goes through the camera; a node under engine.screen2d does not. Place a screen2d node over a world point with it each frame (a ring over a unit), or anchor an overlay to a world node. Returns a freshly-allocated Vec2. |
fromScreen(x: float, y: float): Vec2 | The inverse of toScreen: maps a point in screen units to this node's local space, through the camera when the node is under the world root. Returns a freshly-allocated Vec2. |
remove(): void | Removes this node from its current parent. No-op if already detached. |
removeAllChildren(): void | Removes every direct child from this node in one call. |
removeChild(child: Node): void | Drops the strong reference to child from this node. Inverse of add. No-op if child is not a direct child of this node. |
setAnchor(nx: float, ny: float): void | Pivot setter taking normalised 0..1 coordinates relative to the node's source size. (0, 0) is top-left (default), (0.5, 0.5) is the centre. Centring the pivot is what makes a rotating node spin in place instead of swinging around its top-left corner. |
alignPivot(horizontal?: Align, vertical?: Align): void | Moves the pivot to an edge or the centre of the node's current content, Starling's alignPivot: alignPivot() centres it, alignPivot(Align.CENTER, Align.BOTTOM) puts it at the bottom middle (feet of a character). Set once from the current size; a later resize does not move it. horizontal takes Align.LEFT, CENTER or RIGHT; vertical takes Align.TOP, CENTER or BOTTOM; any other value raises RangeError. |
clearRenderLayer(): void | Drops this node's own renderLayer so it inherits its nearest ancestor's again. |
setHitRect(x: float, y: float, width: float, height: float): void | Overrides this node's hit-test area with an axis-aligned rectangle in LOCAL coordinates, replacing whatever custom shape (if any) was set before. Without a custom shape, hit-testing uses the node's default bounding-box rect. |
setHitCircle(centerX: float, centerY: float, radius: float): void | Overrides this node's hit-test area with a circle in LOCAL coordinates. See setHitRect for the default/override relationship. |
setHitPolygon(points: Float32Array | Array<float>): void | Overrides this node's hit-test area with a convex polygon in LOCAL coordinates, given as a flat [x0, y0, x1, y1...] array (at least 3 points). See setHitRect for the default/override relationship. |
clearHitShape(): void | Clears a custom hit-test shape set by setHitRect / setHitCircle / setHitPolygon, reverting to the default bounding-box rect. |
hasHitShape(): boolean | True while a custom hit-test shape set by setHitRect / setHitCircle / setHitPolygon is attached. |
hitShapeContains(x: float, y: float): boolean | Whether the LOCAL-space point (x, y) is inside this node's custom hit-test shape. false when no custom shape is attached. |
setPivot(px: float, py: float): void | Pivot setter in raw pixel coordinates. See setAnchor for the normalised form. |
setPosition(x: float, y: float): void | Sets local position. Equivalent to writing x and y individually but in one call. |
setRotation(radians: float): void | Sets local rotation in radians. Equivalent to writing rotation. |
setScale(sx: float, sy?: float): void | Sets scale. One-arg form sets both axes to the same value. Two-arg form sets them independently. |
setSize(w: float, h: float): void | Resize the node. For primitives (Quad, etc.) this scales relative to the authored base size. For sprites it derives a scaling factor from the texture dimensions. The cached scaleX / scaleY fields update to reflect the result. |
setSkew(sx: float, sy: float): void | Sets local skew on both axes in radians. |
worldBounds(): Rect | Returns this node's world-space bounds as a freshly-allocated Rect. The world transform is refreshed before reading, so a node moved earlier in the same frame returns its post-move bounds. |
worldPosition(): Vec2 | Returns this node's world-space position as a freshly-allocated Vec2. The world transform is refreshed before reading. |
worldToLocal(p: any): Vec2 | Maps a point in world space to this node's local space. Returns a freshly-allocated Vec2. p accepts any { x, y }-shaped value (a Vec2 or a plain object literal), only the numeric x/y fields are read. |
on(name: string, handler: Function): Function | Registers handler for a custom event named name, the Starling/Pixi.js-style escape hatch, layered ON TOP of the typed setters above (onClick, etc.) rather than replacing them: a typed setter and a generic on("click"...) listener both fire for the same real click. Events emitted on a descendant bubble up through every ancestor's listeners for the same name (in registration order at each level), until either the tree root is reached or a listener calls e.stopPropagation(). Returns handler for chaining, same convention as engine.onFrame. |
off(name: string, handler: Function): boolean | Removes a specific handler previously registered for name via on, matched by reference. Returns true if a listener was removed. |
offAll(name?: string): void | Removes every listener registered via on for name. With no argument, removes every listener for every name on this node. |
emit(name: string, data?: any): void | Dispatches name on this node, then bubbles to every ancestor, see on's bubbling note. data (optional) is forwarded as e.data on the Event every listener receives. |
hasListener(name: string): boolean | True if this node has at least one listener registered for name via on. |
Event
ts
class EventPayload handed to a Node.on/emit listener. type is the event name; target is the node emit was originally called on (constant across the whole bubble); currentTarget is whichever node's listener is currently running (changes at each bubble level); data is whatever emit's second argument was, or undefined. stopPropagation() halts the bubble walk before any further ancestor is reached.
Event: properties
| Member | Description |
|---|---|
type: string | The event name passed to emit. |
target: Node | The node emit was originally called on. Constant across the whole bubble. |
currentTarget: Node | Whichever node's listener is currently running. Changes at each bubble level. |
data: any | Whatever emit's second argument was, or undefined. |
Event: methods
| Member | Description |
|---|---|
stopPropagation(): void | Halts the bubble walk before any further ancestor's listeners run. |
Align
ts
enum AlignEdge or centre for node.alignPivot.
Align: values
| Member | Description |
|---|---|
LEFT | The left edge (horizontal). |
CENTER | The centre (either axis). |
RIGHT | The right edge (horizontal). |
TOP | The top edge (vertical). |
BOTTOM | The bottom edge (vertical). |
PauseMode
ts
enum PauseModeHow a node treats a pause above it (node.pauseMode); Godot's process modes.
PauseMode: values
| Member | Description |
|---|---|
INHERIT | Follows its parent (default). |
ALWAYS | Keeps running while ancestors are paused: a pause menu or debug overlay inside the game tree. |
WHEN_PAUSED | Runs only while an ancestor is paused. |
DISABLED | Never runs. |