Skip to content

Recipes ​

Short answers to common tasks. Each links to the guide with the details.

Spin a sprite in place ​

Center its pivot, then turn it. Rotation is in radians.

ts
import { Sprite } from "willow";

class Spin {
    static main(): void {
        const s: Sprite = new Sprite("assets/gear.png");
        s.setAnchor(0.5, 0.5);
        s.setPosition(400, 300);
        engine.root2d.add(s);
        s.onUpdate = (dt: float): void => {
            s.rotation += Math.PI * dt;
        };
    }
}

See Nodes and transforms.

Flash a sprite red when hit ​

ts
import { Sprite, TweenGroup, TweenConfig } from "willow";

class Hit {
    static flash(s: Sprite): void {
        s.tint = Color.hex("#FF4040");
        TweenGroup.color(s, Color.WHITE, new TweenConfig(0.3, Ease.OUT_QUAD));
    }

    static main(): void {
        const s: Sprite = new Sprite("assets/hero.png");
        engine.root2d.add(s);
        App.onFrame((dt: float): void => {
            if (Keyboard.isPressed(Key.H)) {
                Hit.flash(s);
            }
        });
    }
}

See Tweens and transitions.

Keep the HUD still while the world scrolls ​

Add HUD nodes to engine.screen2d, not engine.root2d. See Camera and layers.

Find the world point under the mouse ​

ts
import { Quad } from "willow";

class Pick {
    static main(): void {
        const marker: Quad = new Quad(8, 8, Color.hex("#FFD24A"));
        engine.root2d.add(marker);
        App.onFrame((dt: float): void => {
            if (Mouse.isPressed(Mouse.LEFT)) {
                const p: Vec2 = engine.camera.screenToWorld(Vec2{ x: Mouse.x, y: Mouse.y });
                marker.setPosition(p.x, p.y);
            }
        });
    }
}

Make a button ​

Give a Quad or Sprite an onClick, and change its colour in onPointerEnter and onPointerLeave. See Input and hit testing. For themed buttons, labels and layouts, use willowui.

Remove an enemy for good ​

Call enemy.dispose(). It leaves its parent, its tweens stop and it leaves any NodeIndex. Use remove() instead if you plan to add it back. See The scene graph.

Pause the game but not the pause menu ​

Set paused = true on the game's root node and pauseMode = PauseMode.ALWAYS (or WHEN_PAUSED) on the menu. See Lifecycle and time.

Crossfade to the next level ​

await engine.scene.playTransition(Transition.fade(0.5), oldLevel, newLevel), then dispose oldLevel. See Tweens and transitions.

Draw thousands of bullets ​

Use one SpriteBatch with the bullet texture and move elements by handle. See Sprites and textures.

Find every node with a tag ​

engine.scene.index is the scene's NodeIndex. Register nodes with index.add(node, "enemy"), then index.findByTag("enemy"). See Camera and layers.

Do something after a delay ​

await node.ticker.wait(2) or node.ticker.every(0.5, fn). The timer pauses with the node and is cancelled if the node is disposed. See Tweens and transitions.