Effects
Particles
A ParticleEmitter is a node that spawns and simulates a pool of particles. Set it up with an EmitterConfig.
ts
import { ParticleEmitter, EmitterConfig, Range } from "willow";
class Sparks {
static main(): void {
const cfg: EmitterConfig = new EmitterConfig();
cfg.maxParticles = 300;
cfg.emitRate = 120;
cfg.lifetime = new Range(0.4, 0.9);
cfg.speed = new Range(80, 220);
cfg.angle = new Range(-Math.PI * 0.75, -Math.PI * 0.25);
cfg.gravityY = 400;
cfg.startColor = Color.hex("#FFE08A");
cfg.endColor = Color.hex("#FF4020");
cfg.endAlpha = 0;
cfg.endScale = 0.2;
const sparks: ParticleEmitter = new ParticleEmitter("sparks", cfg);
sparks.setPosition(400, 450);
engine.root2d.add(sparks);
sparks.start();
App.onFrame((dt: float): void => {
if (Keyboard.isPressed(Key.SPACE)) {
sparks.burst(60);
}
});
}
}| Config field | Meaning | Default |
|---|---|---|
maxParticles | Pool size. | 128 |
emitRate | Particles per second while started. | 10 |
lifetime, speed, angle | Ranges for each new particle; angles in radians. | 1 s, 50 px/s, full circle |
gravityX, gravityY | Constant acceleration. | 0 |
startScale / endScale | Scale over a particle's life. | 1 / 1 |
startAlpha / endAlpha | Alpha over a particle's life. | 1 / 0 |
startColor / endColor | Tint over a particle's life. | white |
blendMode | How particles blend. | "add" |
worldSpace | Particles stay where they were born when the emitter moves (a trail). | false |
start() and stop() control continuous emission; burst(n) spawns at once; reset() clears everything. seed (or setSeed) makes the randomness repeatable.
Blend modes
node.blendMode is one of "normal", "add", "multiply", "screen", "erase", "mask", "below" or "none". "add" brightens and suits fire, magic and lasers.
Filters
Filters change how a node and its children look once drawn. A node can have several; they apply in order.
ts
import { Sprite, OutlineFilter, ColorMatrixFilter, BlurFilter } from "willow";
class Filtered {
static main(): void {
const selected: Sprite = new Sprite("assets/unit.png");
selected.setPosition(200, 200);
selected.addFilter(new OutlineFilter(2, Color.hex("#FFD24A")));
engine.root2d.add(selected);
const stone: Sprite = new Sprite("assets/unit.png");
stone.setPosition(400, 200);
stone.addFilter(new ColorMatrixFilter().setGrayscale());
engine.root2d.add(stone);
const far: Sprite = new Sprite("assets/hills.png");
far.setPosition(0, 380);
far.addFilter(new BlurFilter(4));
engine.root2d.add(far);
}
}| Filter | Effect |
|---|---|
BlurFilter(radius) | Blur. |
OutlineFilter(thickness, color) | An outline several pixels thick. |
PixelPerfectOutlineFilter(color) | A crisp one-pixel outline for pixel art. |
PixelPerfectInlineFilter(color) | Recolours the one-pixel edge inside the shape. |
ColorMatrixFilter() | Brightness, contrast, saturation, hue, grayscale, invert. The setters chain. |
PaletteFilter() | Remaps colours by brightness to a 256-colour palette; animate cycleOffset for palette cycling. |
CustomShaderFilter(path) / CustomDrawShaderFilter(path) | Your own prebuilt fragment shader. |
Manage the list with addFilter, removeFilter, clearFilters, setFilters or the filters array.
Cost. Filters marked as draw filters (ColorMatrixFilter, the pixel-perfect pair, PaletteFilter, CustomDrawShaderFilter) can apply while the sprite draws, without an offscreen pass, when the node is a single sprite with no mask or cache. The others (BlurFilter, OutlineFilter, CustomShaderFilter) draw the subtree to an offscreen texture first.
Custom shaders are bgfx fragment binaries built ahead of time with shaderc; there is no shader compiler at run time. willow loads <path>.<backend>.bin for the running renderer. The source image is sampler 0, and setUniform(name, value) sets named uniforms.
Masks
node.setMask(maskNode) clips the node's subtree to the mask's shape. clearMask() removes it. The mask's own transform is ignored, and masks do not affect hit testing.
ts
import { Sprite, Polygon } from "willow";
class Porthole {
static main(): void {
const view: Sprite = new Sprite("assets/ocean.png");
view.setPosition(200, 100);
engine.root2d.add(view);
const circle: Polygon = Polygon.circle(120, Color.WHITE);
view.setMask(circle);
}
}Lighting
A LightLayer darkens everything drawn before it in the tree and cuts lights out of the darkness. Add your UI after it so the UI stays lit.
ts
import { Sprite, LightLayer, Light, Text } from "willow";
class Cave {
static main(): void {
engine.root2d.add(new Sprite("assets/cave.png"));
const hero: Sprite = new Sprite("assets/hero.png");
hero.setPosition(300, 300);
engine.root2d.add(hero);
const dark: LightLayer = new LightLayer(0.85);
engine.root2d.add(dark);
const torch: Light = new Light(0, 0, 160, Color.hex("#FFD9A0"), 1.0);
torch.attachTo(hero, 16, 16);
dark.addLight(torch);
const lamp: Light = new Light(600, 200, 90, Color.hex("#A0C8FF"), 0.8);
dark.addLight(lamp);
engine.root2d.add(new Text("Lit UI"));
}
}ambientDarkness (0 unlit to 1 black) and ambient (the colour of the dark) shape the mood. addLight returns an id for removeLight and hasLight. A light attached to a node follows it.
Render textures
A RenderTexture is a canvas you draw images onto, then show as a sprite. Draws accumulate, which suits paint tools, decals and minimaps.
ts
import { RenderTexture, Sprite, Texture } from "willow";
class Decals {
static main(): void {
const canvas: RenderTexture = new RenderTexture(800, 600);
engine.root2d.add(canvas.createSpriteNode("decals"));
const splat: Texture = Texture.load("assets/splat.png");
App.onFrame((dt: float): void => {
if (Mouse.isPressed(Mouse.LEFT)) {
canvas.drawImage(splat, Mouse.x - 16, Mouse.y - 16);
}
});
}
}drawImageTransformed and drawSpriteTransformed take a RenderTextureDrawOpts with position, scale, rotation, pivot, colour, alpha and blend. fill(color) and clear() reset the canvas. Every sprite made with createSpriteNode shows the live canvas.
ToTexture(node) renders a subtree once into a new RenderTexture, for a snapshot or a thumbnail. Call it between frames (from onUpdate), and dispose the result when done.
Next
- Rope and distortion
- Reference: ParticleEmitter, Filters, LightLayer, RenderTexture