Shapes and meshes
willow draws shapes without any image: rectangles, lines, polygons and raw triangle meshes. They are ordinary nodes, so they move, rotate, fade and take input like sprites.
Rectangles and lines
ts
import { Quad } from "willow";
class Bars {
static main(): void {
const back: Quad = new Quad(200, 20, Color.hex("#3A1010"));
back.setPosition(20, 20);
engine.root2d.add(back);
const health: Quad = new Quad(200, 20, Color.hex("#E04040"));
health.setPosition(20, 20);
health.scaleX = 0.6;
engine.root2d.add(health);
const laser: Quad = Quad.line(100, 300, 500, 220, 3, Color.hex("#7CF2FF"));
laser.blendMode = "add";
engine.root2d.add(laser);
}
}new Quad(w, h, color)is a solid rectangle. Change it withcolor,widthandheight.- Scaling a bar with
scaleXfrom its left edge is the easy way to draw a meter. Quad.line(x1, y1, x2, y2, thickness, color)makes a line as a thin rotated quad, with the position and rotation set for you.
Polygons
Polygon fills a shape given as a flat list of points [x0, y0, x1, y1, ...]. Factories cover the common shapes:
| Factory | Shape |
|---|---|
new Polygon(points, color) | Any simple polygon, at least three points. |
Polygon.regular(sides, radius, color) | A regular polygon centered on the origin. |
Polygon.circle(radius, color) | A circle (a 32-sided regular polygon). |
Polygon.star(points, outer, inner, color) | A star. |
Polygon.triangle(x1, y1, x2, y2, x3, y3, color) | A triangle. |
Polygon.textured(texture, points) | A polygon filled with an image, mapped across its bounding box. |
ts
import { Polygon } from "willow";
class Shapes {
static main(): void {
const sun: Polygon = Polygon.circle(40, Color.hex("#FFD24A"));
sun.setPosition(120, 120);
engine.root2d.add(sun);
const star: Polygon = Polygon.star(5, 30, 12, Color.WHITE);
star.setPosition(300, 120);
engine.root2d.add(star);
const hill: Polygon = new Polygon([0, 600, 0, 450, 200, 380, 450, 430, 800, 360, 800, 600], Color.hex("#2F6B3A"));
engine.root2d.add(hill);
App.onFrame((dt: float): void => {
star.rotation += dt;
});
}
}Polygon colours are full Color values with alpha. If you write a raw number, use all eight hex digits (0xRRGGBBAA): a six-digit number has an alpha of 0 and is invisible.
Replace a polygon's points with setPoints(points, color?).
Meshes
Mesh draws your own triangles. Vertices are flat, eight numbers each: x, y, u, v, r, g, b, a (colours 0 to 1). Indices list three vertices per triangle.
ts
import { Mesh } from "willow";
class Gradient {
static main(): void {
const vertices: Array<float> = [
0, 0, 0, 0, 1, 0, 0, 1,
300, 0, 1, 0, 0, 1, 0, 1,
300, 200, 1, 1, 0, 0, 1, 1,
0, 200, 0, 1, 1, 1, 0, 1,
];
const indices: Array<int> = [0, 1, 2, 0, 2, 3];
const quad: Mesh = new Mesh(vertices, indices);
quad.setPosition(250, 200);
engine.root2d.add(quad);
}
}tint multiplies every vertex colour at draw time, so recolouring a mesh does not rebuild it. After editing vertices with setVertices, willow updates the bounds; call invalidateBounds() only if you changed them some other way.
Custom paint
For full control, give a node a paint function with setCustomPaint. It runs each frame instead of the node's normal drawing and receives a Painter.
ts
import { Node, Painter, TrianglesPaint } from "willow";
class Painted {
static main(): void {
const node: Node = new Node("painted");
node.setPosition(400, 300);
engine.root2d.add(node);
node.setCustomPaint((painter: Painter): void => {
const m: Array<float> = painter.worldTransform();
const px: Array<float> = [0, -40, 40, 30, -40, 30];
const verts: Array<float> = [];
for (let i: int = 0; i < 3; i++) {
const x: float = px[i * 2];
const y: float = px[i * 2 + 1];
verts.push(m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5], 0, 0, 1, 0.5, 0, 1);
}
painter.appendTriangles(new TrianglesPaint(verts, [0, 1, 2]));
});
}
}- Painted vertices are in world space.
worldTransform()returns[a, b, c, d, tx, ty]; a local point(x, y)maps to(a*x + c*y + tx, b*x + d*y + ty). The camera is applied afterwards. painter.paintDefault()draws the node's normal content too, so you can add to a sprite rather than replace it.painter.isBuildingCache()is true while a cached subtree is being recorded; skip animated extras then.- The
Painteris only valid during the call. Don't keep it. - Pass
nulltosetCustomPaintto remove the function.
Next
- Tweens and transitions
- Reference: Quad, Polygon and Mesh, Custom paint