Nodes and transforms
Every node has a local transform: where it sits, how it is scaled and rotated, relative to its parent. willow multiplies the transforms down the tree to get each node's world transform. Only nodes whose transform (or an ancestor's) changed are recomputed, so a still subtree costs nothing per frame.
Transform fields
| Field | Meaning | Default |
|---|---|---|
x, y | Position in the parent's space, in pixels. | 0 |
scaleX, scaleY | Scale factor. 1 is the authored size; negative flips. | 1 |
rotation | Rotation in radians, clockwise on screen. | 0 |
skewX, skewY | Skew in radians. | 0 |
pivotX, pivotY | The point, in the node's own pixels, that x/y place and that rotation and scale turn around. | 0 (top-left) |
alpha | Opacity 0 to 1. Multiplies with the parent's alpha. | 1 |
visible | false hides the node and its whole subtree. | true |
Shortcuts set two fields at once: setPosition(x, y), setScale(s) or setScale(sx, sy), setSkew(sx, sy), setRotation(r), setPivot(px, py).
Pivots and anchors
By default a node's origin is its top-left corner, so it rotates around that corner. Move the pivot to make it spin in place:
| Call | Pivot |
|---|---|
setAnchor(0.5, 0.5) | The center, as a fraction of the node's size. |
setAnchor(0.5, 1.0) | Bottom middle: the feet of a character. |
setPivot(16, 32) | An exact pixel inside the node. |
alignPivot() | The center of the current content. |
alignPivot(Align.CENTER, Align.BOTTOM) | Bottom middle of the current content. |
alignPivot reads the size once. If the content later changes size, call it again.
setSize(w, h) resizes a node by changing its scale: a 64x64 sprite given setSize(128, 32) gets scaleX = 2 and scaleY = 0.5.
Containers carry their children
ts
import { Node, Quad } from "willow";
class Carry {
static main(): void {
const ship: Node = new Node("ship");
ship.setPosition(100, 50);
ship.setScale(2);
const turret: Quad = new Quad(20, 20, Color.WHITE);
turret.setPosition(10, 5);
ship.add(turret);
const p: Vec2 = turret.worldPosition();
console.log("turret in the world: " + p.x + ", " + p.y);
ship.x = 300;
const q: Vec2 = turret.worldPosition();
console.log("after moving the ship: " + q.x + ", " + q.y);
}
}Output:
text
turret in the world: 120, 60
after moving the ship: 320, 60The turret's own x and y never changed. Moving, scaling or rotating ship moves everything inside it.
Converting between spaces
| Method | From | To |
|---|---|---|
node.localToWorld(p) | The node's local space | World space |
node.worldToLocal(p) | World space | The node's local space |
node.toScreen(x, y) | The node's local space | Screen pixels (through the camera for world nodes) |
node.fromScreen(x, y) | Screen pixels | The node's local space |
node.worldPosition() | The node's origin in world space | |
node.worldBounds() | The node's bounding Rect in world space | |
a.distanceTo(b) | World distance between two nodes, in pixels | |
a.directionTo(b) | Unit Vec2 from a toward b |
localToWorld and worldToLocal accept any { x, y } value. Each call returns a new Vec2.
ts
import { Node } from "willow";
class Spaces {
static main(): void {
const map: Node = new Node("map");
map.setPosition(200, 100);
map.setScale(0.5);
const local: Vec2 = map.worldToLocal(Vec2{ x: 300.0, y: 150.0 });
console.log("a click at (300, 150) is (" + local.x + ", " + local.y + ") on the map");
const hero: Node = new Node("hero");
const goal: Node = new Node("goal");
hero.setPosition(0, 0);
goal.setPosition(30, 40);
console.log("distance: " + hero.distanceTo(goal));
}
}Output:
text
a click at (300, 150) is (200, 100) on the map
distance: 50Names and user data
name is a free-form label, useful in logs and for lookups with a NodeIndex. userData holds any value you want to attach to a node; willow never reads it.
Next
- Sprites and textures
- Node reference lists every field and method.