Modules / Game flow & UI

renderdomplugin

An HTML layer over the game: panels, canvases, pointer surfaces, keys and a kit of controls.

Putting it in a scene

List it in plugins. It has no row and no settings: it reads the game's size from the renderer and draws nothing until an actor asks for it.

"plugins": ["loader", "scenemanager", "player", "render2d", "renderdom"]

The plugin lays one element, its root, over the game's canvas: render3d's canvas, else render2d's, once that canvas is on the page and laid out. With neither, it is centred in the window at the largest size that fits. The root is the game's size in its own units, the renderer's size where it gives one, else the engine prefab's width × height, and is scaled to the canvas every tick, so it follows the page as it is resized. Every box below is in those units. The root takes no pointer input itself, so the canvas under it is clicked wherever no panel lies. It is shown from the end of the first frame, so it is never seen empty. It needs a page: on a client with no document, as in Node, it throws on its first tick.

Everything on it is asked for every frame, in an actor's tick. A panel or element asked for is kept; one not asked for in a frame is taken off the page at the end of that frame.

Methods

On the page, through plugins.renderdom. A box is [x, y, width, height], from the top left.

panel( name, box ) : HTMLDivElement

The panel's element, made the first time it is asked for and placed at box each frame. A panel takes the pointer over its whole box. Panels lie over one another in the order they were first made. Everything shown on a panel is placed in the panel's units, (0, 0) its top left.

show( panel, key, make ) : element

The element for key on a panel asked for this frame. make makes it the first time; the same element comes back every frame after, and the caller places and fills it each frame. A key may be asked for once a frame. The elements of a panel lie in the order they were asked for, the first under the rest. Each carries its key as data-key, and its panel its name as data-panel.

canvas( panel, key, box ) : CanvasRenderingContext2D

A canvas at box, kept as show keeps an element, cleared each frame it is asked for. Its 2D context, the same object every frame, draws in the box's own units: (0, 0) its top left, (width, height) its bottom right. Its pixels are the box's size times the root's scale times the screen's pixel ratio, rounded, and are resized when either changes.

surface( panel, key, box, hands ) : HTMLDivElement

Pointer input over box, a transparent element kept as show keeps one. Each event is handed to hands at the point in the box's own units, with mods, { shift, alt, mod }, mod being Ctrl or Cmd. The hands given in the latest frame are the ones called, and a hand left out is not called.

HandCalled when
down( x, y, mods )The left button is pressed on the box, while no surface holds the pointer. The pointer is captured: move and up follow it wherever it goes, so their points can be outside the box.
move( x, y, mods )The captured pointer moves.
up( x, y, mods )The captured pointer is let go, or its capture is lost, or the surface is taken away while held, so every down has one up. Let go outside the box, leave follows it.
hover( x, y, mods )The pointer moves over the box with no button down and no surface holding the pointer.
leave( x, y, mods )The pointer leaves the box, under the same condition.
wheel( x, y, mods, dx, dy )The wheel turns over the box, its deltas in CSS pixels (a line is 16, a page the window's height). The page does not scroll.
menu( x, y, mods )The right button is pressed on the box. The browser's context menu never opens over a surface.

cursor( panel, key, css )

The CSS cursor over a surface shown this frame. While that surface holds the pointer, the cursor stays it wherever the pointer goes.

keys( handler ) / keyups( handler )

Hands handler every key going down, or let go, on the page, as { key, code, ctrlKey, metaKey, shiftKey, altKey, mod, repeat }, while it is asked for every frame. Several asked in one frame are each handed every key, in the order they were asked. A handler that returns true took the key, and the browser's own action for it, such as a scroll for Space, is cancelled. A key typed into an input, a textarea or anything contenteditable reaches no handler; one typed into such a field on a panel reaches no other listener of the page either, and the field still takes it.

style( css )

A stylesheet for the elements in the root alone. Each selector is put under the root, and & in a selector stands for the root itself, as in CSS nesting. The rules inside @media, @supports, @container and @layer are scoped the same, and other at-rules, such as @keyframes and @font-face, are kept as they are. The same string asked for again adds nothing.

fonts( urls )

Adds a stylesheet link per URL to the page's head, once each, such as a Google Fonts URL. Once a stylesheet has loaded, every font face it declares is loaded, so text drawn on a canvas finds it from the frame after.

Properties of the plugin

Also on plugins.renderdom.

root : HTMLDivElement

The element every panel is in.

scale : number

The CSS pixels one of the game's units is drawn across, as of the last tick.

size : [width, height]

The game's size in its own units, as of the last tick.

The controls kit

From #plugins/renderdom/controls.ts. controls( plugins.renderdom ) returns the parts below, and puts their stylesheet and three fonts on the page once: Michroma for names, Silkscreen for labels and Share Tech Mono for values, on a silver panel.

Each part is shown with the same rule as show: asked for every frame, placed in its panel's units with at its top left, and drawn from what it is handed. A part holds no value of its own: it shows the value it is handed and hands an edit to onChange or onPick. Each returns its element. A part also shows its pointer area under a key of its own, <key>:dial, :track, :row or :press, so those keys are taken.

A part that edits takes a gesture, { begin, end }, one step of undo. A drag calls begin before its first change and end when it is let go, however many changes lie between; a drag that changes nothing calls neither. A double click's reset, a wheel's step and a pick are each a gesture of one change.

knob( panel, key, { at, label, value, range, log?, step?, reset, shown, accent, dial?, onChange, gesture, onMenu? } )

A dial with label over it and shown( value ) under it, the value lit in accent round a ring of 270°, from its lower left. Dragging up raises it: 150 units of drag go from the least of range to the most, a tenth of that with Shift held. log spaces the range by ratio, so each doubling is as far as every other, and needs a range above 0. step keeps the value to whole steps from the least. A double click, two presses within 300 ms, sets it to reset. Each turn of the wheel the browser reports moves it one step, or, without a step, 1% of the way, or 0.1% with Shift held. The right button calls onMenu. dial is the dial's diameter, 36 when left out.

knobSize( dial? ) : [width, height]

The room a knob takes: 56 × 60 with the default dial, else as wide as the dial plus 12 and at least 40, and as tall as the dial plus 24.

fader( panel, key, { at, height, label, value, range, log?, step?, reset, shown, accent, onChange, gesture, onMenu? } )

A vertical fader 28 units wide and height tall, its cap on a track that runs the height less 38, from the least of range at the bottom to the most at the top. Dragged, double clicked, wheeled and right-pressed as a knob is, the whole track being the travel.

switch( panel, key, { at, width, label, options, on, onPick, gesture } )

A row of options, width units across, the option on lit with an LED. A press on another option picks it. Each segment is as wide as its label needs and the room over that is shared out evenly. A width under switchWidth( options ) throws, since a label would run past its segment. segmentWidths( options, width ) gives each segment's width.

button( panel, key, { at, size, label, lit?, accent?, onPress } )

A key-cap that is drawn pressed while held and calls onPress when let go over it. With lit given it carries an LED, lit in accent while lit is true.

led( panel, key, { at, on, accent } )

A lamp 8 units across, lit in accent while on.

lcd( panel, key, { at, size, lines } )

A green screen with a line of text per entry of lines.

meter( panel, key, { at, size, level } )

A bar fed a level in dBFS each frame, filled from −60 dB to +6 dB, red over 0 dB. The highest level of the last 1.5 seconds is held as a line, which then falls 20 dB a second.

module( panel, key, { box, title, lit?, accent? } )

A bevelled box with title along its top, and, with lit given, an LED beside the title lit in accent while lit is true. Show it before the controls on it, so they lie over it.

fraction( { range, log? }, v ) / valueAt( { range, log?, step? }, f )

Where a value lies along its range, 0 to 1, and the value at a fraction of the range, kept to its steps: the mapping a knob and a fader draw and drag by.