Skip to content

Gauges, picks and spatial helpers ​

Small helpers that many games need. Each one is independent; use whichever you like.

ts
import { Gauge, LinearSpatialIndex, Transform2D, WeightedPick } from "gamekit";

// Gauge: a value that fills with time you give it. Good for cooldowns and charge bars.
const dash: Gauge = new Gauge(1.0, 0.5);         // capacity 1, fills 0.5 per second
const ready: boolean = dash.advance(2.0);        // true when it reaches full
console.log("dash ready: " + ready);
dash.consume(1.0);

// WeightedPick: a weighted random choice from a seeded Random.
const loot: WeightedPick<string> = new WeightedPick<string>(new Random(42));
loot.add("coin", 80.0);
loot.add("gem", 19.0);
loot.add("crown", 1.0);
console.log("drop: " + loot.pick());

// Transform2D: plain position, rotation and scale data for simulation code.
const t: Transform2D = new Transform2D(Vec2{ x: 10.0, y: 20.0 });
t.rotation = 0.5;
console.log("at " + t.position.x + ", " + t.position.y);

// LinearSpatialIndex: find things by point, area or nearest.
const index: LinearSpatialIndex<string> = new LinearSpatialIndex<string>();
index.upsert("tree", Rect{ x: 0.0, y: 0.0, width: 10.0, height: 10.0 });
index.upsert("rock", Rect{ x: 50.0, y: 50.0, width: 5.0, height: 5.0 });
console.log("at (5, 5): " + index.point(Vec2{ x: 5.0, y: 5.0 }).join(", "));
console.log("nearest to (40, 40): " + (index.nearest(Vec2{ x: 40.0, y: 40.0 }) ?? "none"));
text
dash ready: true
drop: coin
at 10, 20
at (5, 5): tree
nearest to (40, 40): rock

Gauge ​

A value between 0 and capacity that fills at rate per unit of time you pass to advance(dt). Use it for cooldowns, charge bars and stamina.

MemberMeaning
new Gauge(capacity, rate, start = 0)Throws InvalidArgument if capacity is not above zero or rate is negative.
advance(dt)Fills and returns true when this call makes it full. Does nothing while paused.
consume(amount), fill()Spend some, or fill it at once.
value, capacity, rate, pausedPlain fields.

Because time is passed in, a crossing happens on the same tick in every run. The cooldown recipe uses one in a system.

WeightedPick ​

new WeightedPick<T>(random) picks items with probability proportional to their weight, from the seeded core Random you give it. fork(name) copies the entries onto a new picker that draws from random.fork(name); later add calls on either one do not affect the other.

Transform2D and the spatial index ​

  • Transform2D(position, rotation, scale) is plain simulation data. It never touches a willow node; copy it onto the screen in your presentation code.
  • LinearSpatialIndex<T> implements ISpatialIndex<T>: upsert(value, rect), remove, point, area, nearest(point, maxDistance?) and swept(rect, delta). It checks every entry, which is fine for small sets; implement ISpatialIndex for a grid or tree when you have many.

Music ​

The audio family's MusicDirector owns what music is playing. play(track) changes it (and skips a change to the track already playing), push(track) plays one on top and pop() returns to the one below, with a crossfade of the length you choose. It talks to sound through an IMusicBackend, so tests can pass a fake. In a game, forward to the core Audio system:

ts
import { Audio, MusicTrack } from "audio";
import { IMusicBackend, MusicDirector } from "gamekit";

// Forwards to the core Audio system.
class CoreMusic implements IMusicBackend {
    async loadMusic(trackId: string): Promise<any> { return await Audio.loadMusic(trackId); }
    playMusicHandle(handle: any, volume: float, loop: boolean): void { Audio.playMusicHandle(handle as MusicTrack, volume, loop); }
    crossfadeTo(handle: any, durationMs: int, volume: float, loop: boolean): void { Audio.crossfadeTo(handle as MusicTrack, durationMs, volume, loop); }
    unloadMusic(handle: any): void { Audio.unloadMusic(handle as MusicTrack); }
}

const music: MusicDirector = new MusicDirector(new CoreMusic(), 800);

async function main(): Promise<void> {
    await music.play("music/town.ogg");
    await music.push("music/battle.ogg");   // town is kept underneath
    await music.pop();                      // crossfades back to town
}

await main();

The project lists the audio system: systems: ["willow", "bitmap", "audio"].

Reference: Gauge, WeightedPick, Transform2D, LinearSpatialIndex, MusicDirector.