uiplugin
The HUD: its own PixiJS stage over the game, with panels, buttons, bars, modals and focus.
Putting it in a scene
The HUD is a PixiJS stage of its own, drawn by its own renderer on a transparent canvas that the page lays over the game's canvas in the same grid cell and at the same size. That size is the game renderer's size, which is the engine prefab's width and height, or the window's under render2d's fit: "window". A scene with nothing but a UI, a menu, names no game renderer and takes the engine prefab's size. The renderer draws at the screen's pixel ratio, so the game may draw at any size under it and the UI stays sharp. When the window is resized under fit: "window", the canvas takes the new size and every panel is placed again by its anchor. When render2d's fit scales the game's canvas, the UI's canvas is sized on the page by the same scale, so it still lies over the game's. A local actor that shows things is listed after ui, because the UI reads world.ui in its tick.
"plugins": ["loader", "scenemanager", "player", "render2d", "ui"],
"world": {
"ui": {
"keys": { "up": ["ArrowUp", "w"], "down": ["ArrowDown", "s"], "left": ["ArrowLeft", "a"], "right": ["ArrowRight", "d"], "press": ["Enter", " "], "back": "Escape" },
"panels": {
"hud": { "anchor": "top-left", "at": [8, 8], "size": [200, 48] },
"pause": { "anchor": "center", "size": [240, 160], "layer": 10, "modal": true, "backdrop": { "color": 0, "alpha": 0.5 }, "key": "Escape", "group": "menus" }
},
"styles": {
"title": { "fontFamily": "monospace", "fontSize": 24, "fill": "#ffffff", "stroke": { "color": 0, "width": 3, "join": "round" } }
}
}
}
The UI has no row, so world.ui is read off the scene and never sent. A world.ui in the manager scene is every scene's until a scene gives its own.
Properties
Read from world.ui.
.keys : { up, down, left, right, press, back }
The keys the UI acts on, each one key or a list of them. up, down, left and right move the focus, press presses the focused thing, and back goes out of a modal. Name all six. A key is named by its place on the keyboard, not by the character it types: a, ' ', Enter, Escape, ArrowUp. A gamepad's keys, Pad0 and the rest, are named the same way, and reach the UI when player plays by actions.
.panels : { [name]: Panel }
Everything the UI draws is on a panel, a plain PIXI.Container named here.
| Field | Meaning |
|---|---|
anchor | Required. A point of the canvas and the same point of the panel, placed together: top-left, top, top-right, left, center, right, bottom-left, bottom or bottom-right. |
at | [x, y] pixels the panel is moved from its anchor. Default [0, 0]. |
size | [width, height] of the panel's box, in the panel's own pixels. An actor reads it from the container's boundsArea to fill the panel. clip and drawing on a surface need it. |
clip | True hides whatever is drawn outside the box. |
scale | Canvas pixels to each of the panel's own, default 1. size, clip and every place an actor puts a thing are in the panel's own pixels, while anchor and at place the scaled box on the canvas. Art drawn for a smaller screen is laid out in its own pixels and shown as big as the canvas wants. |
layer | Draws the panel over every panel with a lower layer, whatever order the actors ran in. Default 0. Of two panels on one layer, the one listed later is drawn over the other. |
modal | True makes the panel a modal (see Modals, menus and focus). |
backdrop | The fill of a modal's see-through cover of the canvas, a PixiJS fill such as { "color": 0, "alpha": 0.5 }. Fully transparent when left out. |
key | A key that opens the panel when no modal has the input, and closes it again when it is the modal that has the input. |
group | Of the panels that name one group, one is open at a time. |
screen | The colour a panel drawn on a surface is cleared to, black when left out. |
.styles : { [name]: object }
Named options objects, each passed as it is to PixiJS or to button. style(name) hands back a copy of one. A text style's outline, stroke, carries "join": "round": the canvas mitres an outline's corners by default, and at a glyph's sharp points they run out into spikes.
Modals, menus and focus
| Topic | How it works |
|---|---|
| Modal | A panel with modal: true has the input while it shows anything. A see-through backdrop the size of the canvas lies under what it shows, so no click reaches anything under it and hit is true everywhere. Every key goes to the UI, back excepted on a screen the game put up (below). player lets go of every key the game held and of the pointer lock. Of two modals showing things, the one on the higher layer has the input and the other waits under it. |
| A screen the game puts up | A modal its actor shows behind the game's own if, on a row's field, and it goes when the row says otherwise. It was not opened, and only a row can take it down, so back is not the UI's there: player hears it as a game key. A game that binds that key to an act that clears the row's if, a pause the server holds say, backs out of its screen with the key that backs out of a menu. The panel's own key does nothing there. |
| Menus the player opens | open, dismiss and opened hold which panels are open. The menu's actor shows it behind if (ui.opened(name)). back closes the modal that has the input when the player opened it, and so does that panel's own key. Opening a panel closes the others of its group. The open panels belong to the page's client, so a scene the seat moves to starts with none open. |
| Focus | The focused thing is sent focus and the one it left blur, which is how a button shows it. When a modal takes the input, the focus goes to the first interactive thing it shows, or back to what it had focused when a modal over it has closed. If the focused thing goes while the modal keeps the input, the focus goes to the first interactive thing it shows then. The pointer over an interactive thing focuses it, and over nothing takes the focus away when no modal has the input. |
| Keys with a modal up | up, down, left and right move the focus to the nearest interactive thing in that direction. press sends the focused thing pointertap, as a click does. A key already held when the modal took the input, and every repeat of a held key, is taken and does nothing until it is let go, so holding back pauses a game once instead of flipping it with every repeat. |
| Rebinding | While player listens for a key to rebind an action to, the UI takes none. |
Drawn between steps
Each frame, just before it draws, every thing on the stage, and on a panel drawn on a surface, is put between where the last two steps left it: its position in a straight line, its turn the short way round, and its scale. Once the frame is drawn each is put back where it was set. A dial's hand turned from a row turns as smoothly as the game's own things move under it.
Methods
On the page, through plugins.ui.
Showing things
show( panel, key, make ) : T
Puts a thing on a panel, in an actor's tick, every frame it wants the thing shown. make runs the first time and returns the PIXI container to show, and the same object comes back every frame after that, for the actor to write this frame's values on. The thing's PixiJS label is key, so anything that reads the panel finds it. After a frame in which nobody showed it, it is destroyed with its children and its Graphics contexts. A texture it draws is the loader's and stays. So a row that ends takes its UI with it, and a thing drawn only sometimes is shown behind an if. A key is shown once a frame on a panel. show is the only way onto a panel. Something that cannot be seen was not shown this frame, or was shown off the canvas, under another panel or pinned behind the camera.
panel( name ) : PIXI.Container
The panel's container. Its boundsArea is its size.
style( name ) : object
A copy of one of styles.
project( at ) : { x, y, behind }
The canvas point of a world point, from whichever renderer the scene names: render3d's project through its camera, or render2d's through its world and camera.
pin( c, at )
Puts the shown thing c at the canvas point of the world point at, measured after every actor has ticked, so a nameplate is placed by the camera the frame is drawn with and not by the one before it. A thing pinned where the camera draws nothing is not shown. Call it every frame.
Menus and input
open( name ) / dismiss( name ) / opened( name ) : boolean
Open a panel, close it, and ask whether it is open. opened returns a boolean.
modal( ) : string | undefined
The name of the modal that has the input, or undefined. player asks it each frame.
hit( x, y ) : PIXI.Container | null
The thing under a canvas point that takes the pointer, or null. While a modal is up, its backdrop is the answer wherever nothing on it is. x and y are canvas pixels from the top left: a click's offsetX and offsetY on the game's canvas, divided by the renderer's scale when render2d's fit scales the canvas. player asks it before turning a click or mouse button into a game message.
took( e ) : boolean
Whether the UI took this key event, so it is not a game key. The UI hears every key first. player asks before it treats a key as a game key.
keyDown( key ) : boolean / keyUp( key )
A gamepad's key going down, as player hands it on when it plays by actions, and the same key coming up. keyDown returns whether the UI took it, so it is not a game key.
focused( c ) : boolean
Whether c has the focus.
Surfaces
surface( name ) : { canvas, box } | undefined
Asked in a frame, draws that panel on a surface for the frame instead of over the game, so a 3D renderer can read the canvas as a picture, such as a screen in a cockpit. The panel needs a size and must not be a modal. It leaves the stage, so it is neither drawn on the UI's canvas nor hit. At the end of the frame it is drawn on the surfaces' canvas, one canvas every surface shares, with one upload a frame for every screen it feeds. The surfaces asked in a frame lie side by side along the canvas's top, in the order world.ui lists them, each its size times its scale across at the UI's pixel ratio, a pixel apart, on the panel's screen colour. box is [x, y, width, height], where the panel lies on the canvas in its pixels as the frame ends. It is the same object every frame, and undefined where there is no page to draw on. A frame that does not ask for it puts the panel back in its place by its anchor. Everything on it is shown with show.
Pieces
Actors write plain PixiJS: Graphics, Text, Sprite, Container and PixiJS's own pointer events. These three are added.
button( o ) : PIXI.Container
A background and a label centred on it, which the pointer hits as one thing. It is drawn with focus while the UI has it focused and with fill otherwise. What it does is the actor's: on('pointertap', ...) hands the button back, so a button that does something is one expression.
| Option | Meaning |
|---|---|
size | [width, height] in pixels. |
radius | Corner radius in pixels, 0 when left out. |
fill, focus | PixiJS fills of the background, unfocused and focused. |
text | Text style options for the label. |
label | The label's string. |
bitmap | True makes the label a BitmapText, so a style whose fontFamily is a loaded bitmap font's name draws in that font. |
bar( o ) : PIXI.Container & { set(k, fill) }
A track size [width, height] pixels across, with a frame edge pixels thick all round it in frame (black when left out; edge is 0 when left out). Its top left is the track's, so the frame lies outside it. track is the track's fill. set(k, fill) fills the part k of the track, 0 to 1, from its left, in fill, to a whole pixel.
iris( o ) : PIXI.Graphics & { set(x, y, r) }
A cover of a box size [width, height] pixels across in fill, black when left out. set(x, y, r) lets the circle of radius r pixels about the point (x, y) of the box show through, cut a row of pixels at a time, so its edge steps a whole pixel as the circle a SNES game closes on its hero does. An r of 0 covers the box, and one past its far corner from (x, y) covers none of it.
Properties of the plugin
Also on plugins.ui.
PIXI
PixiJS. Draw with plugins.ui.PIXI rather than importing it.
stage : PIXI.Container
The root container of the UI's stage.
canvas : HTMLCanvasElement | undefined
The UI's canvas, undefined until the page has mounted one. A client with no page, such as a test or a bot, shows, destroys, places and hit-tests the same things and draws and listens for nothing. The canvas stays hidden until the first frame that has anything to draw.
Used by
- cutsceneDraws its bars, veil and line on the panels
cutscene.top,cutscene.bottom,cutscene.veilandcutscene.line. - chatShows its panel and takes lines on it.
pancamera and party also use it.