Packages
Optional, composable pieces you can add to a game. They are subpaths of the quantum-forge-engine package, so using one is an import rather than an install.
Two of them need an optional peer dependency first:
npm install pixi.js # PixiRenderer
npm install howler # AudioManagerAvailable packages
| Package | Import | Description |
|---|---|---|
| Input | quantum-forge-engine/input | Keyboard, mouse, gamepad, touch, gestures, local multiplayer |
| Collision | quantum-forge-engine/collision | Rect, circle, and point tests plus a SpatialGrid |
| Audio | quantum-forge-engine/audio | Howler.js wrapper for sounds and music |
| Particles | quantum-forge-engine/particles | Burst, trail, and explosion effects |
| Animation | quantum-forge-engine/animation | Tweening with easing functions |
| Entities | quantum-forge-engine/entities | Entity registry with spatial queries |
| State machine | quantum-forge-engine/state-machine | Config-driven finite state machine |
| Timer | quantum-forge-engine/timer | Game-aware timers, pause- and scale-aware |
| Save | quantum-forge-engine/save | Slot-based saves with versioning and auto-save |
| Scenes | quantum-forge-engine/scenes | Stack-based scene lifecycle |
Collision
Collision is a set of pure functions plus a grid for narrowing candidates.
import { SpatialGrid, circleOverlaps } from "quantum-forge-engine/collision";
const grid = new SpatialGrid<Enemy>(64);
for (const enemy of state.enemies) grid.add(enemy);
for (const other of grid.getNearby(player.x, player.y)) {
if (circleOverlaps(player, other)) {
// handle collision
}
}getNearby(x, y) takes coordinates and returns everything in that cell and the eight around it. Confirm real overlaps with rectOverlaps, circleOverlaps, rectCircleOverlaps, or one of the point tests.
Audio
import { AudioManager } from "quantum-forge-engine/audio";
const audio = new AudioManager({ soundVolume: 0.8, musicVolume: 0.4, logger });
await audio.loadSound("hit", "/sounds/hit.mp3");
audio.play("hit");
audio.setMasterVolume(0.8);
audio.mute(); // no argument
audio.unmute();Browsers block audio until the player interacts with the page. isUnlocked() reports whether that has happened, which is what a "tap to enable sound" prompt keys off.
Particles
import { ParticleSystem } from "quantum-forge-engine/particles";
const particles = new ParticleSystem({ gravity: 200 });
particles.emit({ x, y, count: 20, color: "#a855f7", speed: 100, life: 0.5 });
particles.burst(x, y, Math.PI / 2, 20, "#a855f7"); // positional arguments
// In game loop
particles.update(dt);
particles.render(ctx); // Canvas 2D
particles.renderToGraphics(this.graphics); // PixiJSburst, trail, and explode take positional arguments. emit(config) is the options-object entry point, and its lifetime field is life, in seconds.
Animation
import { Tween, MultiTween, Easing } from "quantum-forge-engine/animation";
const fade = new Tween({
from: 0,
to: 1,
duration: 500, // milliseconds
easing: Easing.easeOutCubic,
onUpdate: (value) => { sprite.alpha = value; },
});
fade.start();
function frame(now: number) {
fade.update(now); // pass a timestamp, not a delta
if (fade.isActive()) requestAnimationFrame(frame);
}Tween animates one number. For object-shaped animations use MultiTween with a values map:
const move = new MultiTween({
values: { x: { from: 0, to: 100 }, y: { from: 0, to: 200 } },
duration: 500,
easing: Easing.easeOutCubic,
onUpdate: (values) => { sprite.x = values.x; sprite.y = values.y; },
});Easings live on the Easing object. AnimationManager starts animations for you and updates them all from one call.
Save
import { SaveManager } from "quantum-forge-engine/save";
const saves = new SaveManager<GameState>({ prefix: "my-game", version: 1 });
saves.save("slot-1", engine.getState());
const data = saves.load("slot-1");
saves.enableAutoSave("autosave", () => engine.getState(), 30); // seconds
saves.update(dt); // in the game loop, drives auto-saveEvery method takes a named slot. Auto-save is turned on with enableAutoSave() rather than a constructor flag, and it advances only when you call update(dt).
Scenes
import { SceneManager, fadeTransition } from "quantum-forge-engine/scenes";
const scenes = new SceneManager<GameContext>({ logger });
await scenes.push("menu", menuScene, context);
await scenes.push("game", gameScene, context, fadeTransition(0.3, drawFade));
await scenes.pop(); // back to menu, which gets onResume()There is no register() step: you pass the scene object to push() or replace(). A scene implements update(dt) and render(), and may add onEnter, onExit, onPause, and onResume. Drive the stack by calling scenes.update(dt) and scenes.render() each frame.