Headless
Back to the reference

Headless

The game run from Node or Bun with no browser, a frame at a time and the same every time: for tests, scripts and servers that check what a game does.

5 symbols

nacatamalon/headless

createHeadlessGamefunctionnacatamalon/headless

createHeadlessGame(options: THeadlessGameOptions) => THeadlessGame

Makes a game that runs with no browser and no screen, in Node or Bun: the same scenes, hooks and updates as in a page, moved a frame at a time by whoever holds it. Everything the game would show is worked out, so a test can ask where things are and what is shown, and capture() draws the screen without a graphics card when a picture of it is wanted.

It is the same every time it runs. Every frame is one step of the game (step, 60 a second unless it says), the random numbers start from seed (1 unless it says), and keys are pressed and let go between frames, exactly when they are asked to be.

What a game loads is read from the assets folder (assets, default 'public'): a picture's size and pixels when it is a PNG, its other files as they are. Physics runs as in a page, once the game installs it (installPhysics2d(), installPhysics3d()); loaded() waits for its world too. Sound is silent and plays on, so a sound waited on still ends. Without a browser there is no pointer and no gamepad.

Parameters

optionsTHeadlessGameOptions

The game's options, its scenes, and where its files are.

Example

import { expect, test } from 'bun:test';
import { writeFileSync } from 'node:fs';
import { createHeadlessGame } from 'nacatamalon/headless';
import { encodePng } from 'nacatamalon/authoring';
import { Level } from '../src/scenes/level';

test('the player walks right', async () => {
    const game = createHeadlessGame({ width: 320, height: 180, scenes: { Level } });
    await game.loaded();
    const start = game.find('Player')!.transform!.x;

    game.press('ArrowRight');
    game.run(60);
    game.release('ArrowRight');

    expect(game.find('Player')!.transform!.x).toBeGreaterThan(start);
    expect(game.drawn().some((p) => p.text === 'SCORE 0')).toBe(true);
    writeFileSync('level.png', encodePng(game.capture({ scale: 4 })));   // to look at it
    game.destroy();
});
View source · headless/create_headless_game.ts:86

THeadlessCaptureOptionstypenacatamalon/headless

What capture takes.

Properties

regionoptional{ x, y, width, height }

Part of the screen, in game pixels from its top-left corner, each edge taken to the whole pixel at or before it. Left out, all of it.

scaleoptionalnumber

Every pixel as a square this many across, a whole number. 1 by default.

View source · headless/types.ts:141

THeadlessGametypenacatamalon/headless

A game running with no browser and no screen: the same scenes, hooks and updates as in a page, moved a frame at a time by whoever holds it, the same way every time. For checking a game from a test or a script: press keys, run frames, and look at where things are and what is shown.

Properties

framenumber

Frames run so far.

timenumber

Seconds of game run so far: frame steps of 1 / step each.

scenestypeOperator

The scenes running now, by name, in the order they draw.

run
press
release
tap
find
findAll
drawn
loaded
capture
destroy
View source · headless/types.ts:75

THeadlessGameOptionstypenacatamalon/headless

THeadlessGameOptions: Omit<TGameOptions, 'renderer' | 'msaa' | 'scaling' | 'keep' | 'pixelRatio' | 'fullscreenScaling' | 'align' | 'pauseOnBlur' | 'banner' | 'splash'> & { scenes, initialScene, assets }

How a game with no browser is made: the game's own options, its scenes, and where its files are. What only a page has (the canvas's scaling, its pixel ratio, pausing when the tab is hidden, the splash, the startup line) is left out.

Properties

scenesReadonly<Record<string, TSceneFn>>

The scenes, by name, as createGame takes them.

initialSceneoptionalstring

The scene it starts on. Left out, the first one.

assetsoptionalstring

The folder a path the game loads is read from: /assets/hero.png is <assets>/assets/hero.png. Default 'public', from where the process runs, which is where a project made with npm create nacatamalon keeps them.

View source · headless/types.ts:18

THeadlessPicturetypenacatamalon/headless

Something the game shows, as it would be on the screen: a sprite, a text or a panel, where it lands in the game's pixels and how much of it is inside the screen.

Properties

namestring

Its box's name.

kind'sprite' | 'text' | 'panel'
pictureTSprite | TText | TNineSlice

What it draws, as createSprite, createText or createNineSlice gave it.

textoptionalstring

What a text says.

xnumber

The middle of what of it is on the screen, in the game's pixels.

ynumber
leftnumber

The rectangle around all of it, in whole game pixels, turned as it is drawn.

topnumber
widthnumber
heightnumber
seennumber

How much of it is on the screen, from 0 to 1.

View source · headless/types.ts:46