Skip to content

Menus, HUDs and pickups

Entity → UI → Button makes a clickable button: a background sprite, a label and a Button component. Set its Action and a menu needs no code at all: LoadScene (with Scene, e.g. Game), RestartScene or Quit, with a Fade (in seconds) to fade the screen out and back in around the switch. A Shortcut key (Enter for Play, Esc for Back) clicks it too, or a Shortcut Action does: an input action’s name, like OpenMap added in Game → Controls and bound to M, so the key shows in the Controls window and players can change it with a Rebind button. Either one is ignored while the button can’t be clicked, while a panel it isn’t on is open, and (for Enter, Space or A) while another button is picked. Do does any action of the entities named in Targets, as a Trigger does (an RPG hero’s Attack, Cast Spell or Use Item, a door’s Open, with With for an action that takes a value), and is faded while none of them can do it now (a fighter off its turn or without the mana for its spell; a script component says so by implementing IActionReady). In a network game it’s done where the game is run, except on what each machine keeps for itself (a menu, a calibration), which it does there and then. Add a ScreenAnchor to pin anything to an edge or corner of the screen, like a score in the top-left: it stays put whatever the window’s shape, while the camera follows the player or shakes. The Scene view shows it where the game does, on the camera’s 16:9 frame, and dragging it there sets its Offset. For a camera that zooms, set the camera’s HudSize to the Size the HUD is laid out at: everything pinned then keeps its size and place on the screen as the camera zooms in and out, and its buttons click where they’re drawn. Left at 0, the HUD is in world units and zooms with the world. The Scene view outlines buttons’ clickable areas in blue. Menus work without a mouse: the arrow keys, WASD, D-pad or stick pick a button (it lights up), and Enter, Space or the A button click it; with a controller, the top button is picked straight away, and B clicks whichever button Esc would. Untick a button’s Navigable if it sits on a HUD during play, so moving the player doesn’t pick it.

Entity → UI → Slider and Toggle make an options menu with no code. Set a slider’s Setting to Volume, MusicVolume, SoundVolume or AutoSpeed (how quickly dialogue reads on by itself), or a toggle’s to Sound (on or off), Fullscreen, SkipUnread (whether skipping goes through lines not read yet), or Skip or Auto (dialogue skipping or reading on by itself now; see Skip and Auto), and it changes that setting and always shows it. A slider’s Setting can also be the player’s AudioOffset or VisualOffset (how late they hear the sound or see the screen, for Bluetooth headphones and TVs; see Rhythm games), set in milliseconds from its Min to its Max (say -200 to 500, in steps of 5), or their NoteSpeed (how fast rhythm notes come, times what the lanes say: 0.5 to 3, in steps of 0.25). A text inside a slider with Output Value ({0:0} ms) shows how it’s set. The game remembers the player’s choices (whether made by a slider, a toggle or a script) and puts them back next time it runs. For anything else, like a difficulty, set Save As to a name instead and read it from scripts with Game.SaveData.GetFloat("difficulty"). A slider is dragged with the mouse, turned with the wheel, or moved by its Step with left and right once picked; a toggle is a button whose On Text and Off Text become its label (“Sound: On”). Built games go fullscreen with F11 too.

Display settings go in an options menu the same way. A Choice (Entity → UI → Choice) whose Setting is WindowSize offers the common 16:9 window sizes that fit the player’s screen (960x540 up to 3840x2160: on a 1080p screen 1280x720, 1366x768 and 1600x900, since a 1920x1080 window doesn’t fit beside the taskbar) in place of its own Options, and picking one resizes the built game’s window there and then; the window opens at it next time (1280x720 until the player picks one). Fullscreen stays borderless at the screen’s own resolution: Griff never changes the display’s mode, so a window size only counts while windowed (one picked while fullscreen is the window’s once it’s windowed again). One whose Setting is FrameCap offers Off, 30, 60, 120, 144, 165 and 240 frames a second, and a toggle set to VSync turns keeping in step with the screen off and on. With VSync on (the default) the game draws one frame for each the screen shows, as it always has, and a cap skips some to stay under it (30 on a 60 Hz screen draws every other frame); with VSync off it draws each frame as soon as the last is done, as many as the computer can, up to the cap. A slider set to UiScale (say 0.75 to 1.5, in steps of 0.05, with a text showing {0:0%}) draws everything pinned with a Screen Anchor, and tooltips, that many times their size, for a player who wants the HUD bigger or smaller; buttons click where they’re drawn. Camera settings go in the same menu: a toggle set to EdgeScroll and a slider set to PanSpeed (say 0.5 to 2) change how a Camera Controls pans (see the players guide). All four display settings are remembered like the rest, and scripts have them as Game.WindowSize (a WindowSize, null until picked; Game.WindowSizes lists those that fit), Game.VSync, Game.FrameCap (0 for none) and Game.UiScale. The frames are only how often the picture is drawn: the game’s steps stay at its step rate whatever they are, each frame taking the steps its time holds and drawing between the last two, so a capped game plays at the same speed, drawn less often. In the editor’s Game tab and a browser there’s no window to size, so Window Size is kept for the built game and changes nothing there (the choice still lists the sizes that fit this screen, or up to 1920x1080 in a browser); both always keep in step with the screen whatever VSync says (the editor’s own window shares the game’s thread), while the frame cap counts everywhere.

Entity → UI → Panel makes a panel: a backdrop pinned to the middle of the screen. Select it and create buttons, sliders and choices to put them on it. While the game runs it’s hidden until it opens (tick Open to have it open from the start), so you can lay it out in the Scene view. Set its Open On to Pause and it’s a pause menu with no code: Esc, P or Start opens it and stops the game, and pressing it again, Esc or the gamepad’s B closes it (if the panel has a button with the Esc Shortcut, or a Shortcut Action with Esc among its keys, Esc clicks that instead). A button’s Action opens and closes panels too: OpenPanel (with Panel naming one, like Options) and ClosePanel (Resume, Back). SaveGame, LoadGame and DeleteSave (with a Slot, or on a Save Slot that shows it), QuickSave and QuickLoad save the game and load it again, and Continue loads the game saved last (a title screen’s Continue), the scene, the talk and the flags as they were (see Saving and loading). While a panel is open, the mouse, keyboard and gamepad only reach the controls on it, so the HUD and the menu underneath don’t react; its top control (or its First) is picked straight away, and closing it gives the keyboard back to what was picked before. Panels stack: an options panel inside the pause menu closes with it. Pause Game (on by default) stops game time and runs only the scripts on open panels, a line typing out in a dialogue box waits under it, and Interact and the arrow keys don’t reach levers, doors or dialogue (or its choices) behind it; untick it for a game over screen that the game goes on behind. Pauses names what it stops instead of the whole game: Arcade stops only that entity and everything inside it (a minigame on a cabinet, the world behind a shop) while the rest runs on behind the panel (see Pausing part of the game). Close On Back off makes the player choose their way out. Open Message opens it when that message is sent, with no code: Solved from a puzzle’s goals, a GameOver from a player’s Death Message, or anything a pickup, switch or script sends. Needs Flag keeps it shut until a flag is set (FoundMap; not FoundMap for while it isn’t): its Open On key, Open Message and buttons that open it do nothing until then, though its Open action and scripts still open it.

A builder’s Esc puts down the tool in hand before it opens the game menu. A script says so by overriding ClaimsBack (protected override bool ClaimsBack => tool is not null;): while it’s true, Esc and B don’t open a panel on its Open On (P and Start still do) or close one the game goes on behind, so the script reads them itself, and the next Esc, with the tool put down, opens the menu. Only a running script claims Back, not one on a disabled or paused entity or behind a panel that pauses the game, so Esc always closes the pause menu. A press a panel acts on is used up: the Esc or B that closes one, its Open On key opening or closing it, and Enter, A or a Shortcut clicking a button that opens or closes one don’t reach the game’s scripts in that frame, so closing the game menu doesn’t also put the tool down or skip a tour (Input.WasPressed sees the key again only when it’s pressed again, buffered or not). A button with Over Panels ticked keeps working while a panel it isn’t on is open, even one that pauses the game: a menu bar’s Game button opens the game menu over a letter, by a click or its Shortcut Action, on top of it, and closing the menu leaves the letter open. The arrow keys stay on the panel.

Tick Popup for a panel that goes away as soon as the player is done with it: a tool palette’s fly-out (a panel with Pause Game off, inside the palette so it sits beside its icon), a dropdown, a context menu. A mouse press anywhere off it closes it and does nothing else: no button there clicks, scripts don’t see the press, and Game.MouseOnUi is true for it (and for a press on the pop-up’s backdrop), so a builder’s tool doesn’t lay a wall where the player only meant to close the fly-out. Clicking one of its buttons closes it once the button has done its Action and its scripts have heard the click, so a tool picked on it is picked, and an OpenPanel button on it opens its window in the pop-up’s place (keep the window outside the pop-up, or it closes with it). Esc closes it as it closes any panel.

A Pause Group on an entity makes it and everything inside it a group that pauses on its own: a match-3 minigame on an arcade cabinet in a platformer, one player’s half of a split screen, the enemies while a boss talks. While its Paused is ticked, nothing in the group moves, falls, animates, emits, counts down, spawns, shoots, runs its scripts or takes the player’s input (a board in it stands still, a player in it doesn’t move on the arrow keys), but it’s still drawn and still solid to what’s outside it, and the rest of the game runs on. Its Pause, Resume and Toggle actions let a trigger, a wire or a script do it with no code (“when Insert Coin arrives, Resume the Arcade”), and a switch or a wire into it runs it while on and pauses it while off. Pause Message and Resume Message go out as it changes, and its output Paused can be shown or wired. A panel’s or a dialogue’s Pauses pauses a group the same way while it’s open or being said. Scripts call Game.Pause(entity) and Game.Resume(entity) and read entity.IsPaused. What’s paused is worked out on every step from the pause groups, the open panels and the dialogue being said, so rewinding puts it back as it was; online, the host pauses groups as it runs everything else (a panel still can’t pause anything there).

A Game Speed anywhere in the scene sets how fast the game runs, for a sim’s or a strategy game’s speed buttons: its Speed is 1 at normal speed, 4 four times as fast, 0.5 half and 0 paused. Buttons set it with no code: Action Do, Targets the entity it’s on, Do Set Speed With 4 (or 0 for a pause button), or Faster and Slower, which go through its Speeds (1, 2, 4 unless changed; add 16), and a Trigger can do the same on a key’s message. Its Speed is an output, for a Counter or a Text to show. While it’s Exact (the default) a faster game takes more steps of the usual length each frame (Time.FastForward) rather than longer ones, so everything goes as at normal speed, only sooner: workers walking a grid cross as many cells in a game second at 16x as at 1x, bodies fall the same arcs, timers and coroutines finish on the same step, and the game plays out the same at any speed. The camera’s pan keys, fades and menus go by real time and keep their pace. Untick Exact to make steps longer instead (Time.Scale), which costs the machine less; speeds below 1 always make steps shorter. A machine that can’t take that many steps (it spends at most a twentieth of a second of each frame on them) runs the game slower rather than skipping any, and the profiler counts every step. Rewinding steps back through a fast-forwarded game as it does any other, so it reaches back the same game time in less real time. Fast-forward does nothing in a network game, whose steps are kept in step between the players; a script sets Time.FastForward and Time.Scale itself, and a Game Speed doesn’t undo them unless its own speed changes.

Put an action’s name in braces in any text and it shows the key or button that does it, on whatever the player is using: Press {Interact} reads “Press E” on the keyboard and “Press Y” on a gamepad, and follows the player’s own bindings. Braces around anything else are left alone.

A button whose Action is Rebind and whose Input Action is Jump waits for the next key or gamepad button when clicked and makes it jump from then on: a key or mouse button takes the place of Jump’s first key or mouse button, a gamepad button its first gamepad button, and the rest stay, so rebinding Drop Tool (Esc and Right Click) to Q keeps Right Click. Its Binding Slot picks which one it replaces (0 the first, 1 the second, for a row with a primary and a secondary key); a key pressed for a mouse button’s slot (or the other way round) goes in before it, keeping both. Esc cancels. It’s remembered next time. Label it Jump: {Jump} and it always shows the binding (... while it waits); {DropTool:all} shows every key and mouse button of the action (every gamepad button with a gamepad), Esc / Right Click, and {PanUp:1} the one in slot 1. ResetControls puts every action back.

Rather than a Rebind button for each action, add a Controls List to an entity with a Layout, such as an options panel: while the game runs it makes a row for every input action, its name and a Rebind button showing all its keys, which the layout lines up (and scrolls, with Clip). The rows follow the device in use and the player’s rebinding, and are made again when the actions change. Actions picks which to list and in what order (DropTool, Pan*, with * for any letters); empty lists them all, or only the game’s own with Show set to GameOnly. Hide leaves some out (Debug*, Pause), Labels renames them (DropTool = Put Down; otherwise DropTool reads Drop Tool), and Slots set to 2 gives each row a button for its first key and one for its second. The plain rows take a Font, Text Size, Color, Button Color, Row Height and Button Width. For rows of your own, style a row, disable it and name it in Template: each row is a copy, with {name} in its texts for the action’s name, {action} (or {action:all}, {action:1}) for its keys, and a Rebind button with no Input Action rebinding its action. The Scene view shows sample rows where they’ll go; the list’s other children (a heading) are left alone. Scripts read its Listed actions and Rows.

For an on/off switch for each row of a data table (the events that slow the game, say), add a Toggles List to an entity with a Layout: name the Table and while the game runs it makes a row for each table row, its label and a real Toggle button, which the layout lines up. Label Column names the column holding the words (empty shows the row’s name), Default Column a yes or no column saying whether each row starts on (empty starts them all on), and Save As the name each switch is kept under in the save data, with {row} for the row’s name: fire.{row} keeps the Rain row’s as fire.Rain, which scripts read with Game.SaveData.GetBool("fire.Rain") like a hand-placed Toggle. A value the player already saved wins over the table’s default. Hide leaves rows out (Debug*), On Text and Off Text are what the buttons say, and the plain rows take the same Font, Text Size, Color, Button Color, Row Height and Button Width. For rows of your own, style a row with a Toggle, disable it and name it in Template: each row is a copy, with {name} in its texts for the label and {row} for the row’s name, and a Toggle with no Save As is kept under the list’s. The rows are made again when the table changes, and scripts read the list’s Listed row names and Rows.

Entity → UI → Choice makes a button that picks one of its Options (one per row): left and right change it while it’s picked, a click or the mouse wheel moves to the next, and its Format (Difficulty: < {0} >) labels it. With Save As (difficulty) it’s remembered, for scripts to read with Game.SaveData.GetString("difficulty").

Untick a button’s Interactable and it’s drawn faded, ignores the mouse and its shortcut, and is skipped by the arrow keys. Needs Saved keeps it that way until something is saved under that name, for a Continue button that waits for a saved game.

A Show When (Gameplay) on anything (a button, a sprite, a text, a whole toolbar) shows it only while its Needs Flag holds, with no code: a HUD that starts bare and brings in its tools as goals are met. With Needs Flag run.Contracts, the entity and everything inside it are hidden while the game runs until a trigger’s Set Flag, a dialogue’s [set run.Contracts] or a script’s Game.SetFlag("run.Contracts") sets the flag, and hidden again when it’s cleared; not Tutorial shows it only while that flag isn’t set, and run.Contracts or run.Work while either is (a group’s icon that shows once any of its tools is unlocked). “or” works in every Needs Flag: a door’s, a switch’s, a spawner’s, a panel’s. A hidden button can’t be clicked and its Shortcut or Shortcut Action does nothing. It’s looked at every frame, so it decides whether its entity shows: to hide it some other way, hide what it’s inside. The Scene view shows it as ever. A flag named run. lasts one run of the game, and a New Game forgets every flag, so either way an unlock starts hidden again (see Dialogue flags). A Panel has its own Needs Flag, which says when it can open. Zoom Below shows an entity by the camera instead of (or as well as) a flag: with 7 it shows while the main camera’s Size is 7 or less and is hidden when the camera pulls out past that, so a label under each worker (a Text with a Background) is there when the camera is zoomed in on the floor and gone from afar, while the worker’s icon, with no Zoom Below, shows at any zoom. 0, the default, ignores the zoom, an entity needs no flag to go by zoom alone, and with both a Needs Flag and a Zoom Below both have to hold. It shows or hides all at once rather than fading.

A button whose Action is NewGame starts the game over: it forgets the progress kept in the save data (flags, saved inventories, health and counts, what menus saved, best scores) and the scenes remembered as they were left (see Scenes that remember) but not the player’s own settings and controls (volumes, fullscreen, rebound keys), saved games or the lines they’ve read, and goes to its Scene. A game’s own settings are kept too when their names start settings.: a Toggle’s or Slider’s Save As settings.difficulty, or a Toggles List’s settings.fire.{row} (scripts ask SaveData.IsSetting(name)). Flags named run. aren’t kept past the game closing either, so a game that starts straight into play without a New Game button still starts each time with them unset. Give it a Needs Saved and it waits until there’s something to forget. A LoadScene button’s Saved Scene names a value in the save data holding a scene to go to instead of its Scene: a Play button that carries on where the player left off, which goes to Scene until something’s saved there (an adventure’s walker keeps the room it’s in with its Save Room As; see Rooms). A Continue button loads the game saved last, in any slot, as it was (see Saving and loading), an Autosave’s too (see Autosave), and is faded while there’s none. The Adventure template’s menu has New Game and a carrying-on Play; the Visual Novel template’s title screen has New Game and Continue.

Set a sprite’s Slice to how much of its image is border (say 0.25 for a quarter at each edge) and its Slice Size to how big the border is drawn: its corners then stay crisp however you stretch its Size, for menu panels, dialogue boxes and buttons from one small image.

Add a Layout to an entity and it lines up its children, so nothing has to place them: a board of offer cards, a list of quest rows, a shop’s shelf. Its Direction says how: Vertical one under another from the top, Horizontal one beside another from the left, or Grid in rows of Columns, each in a cell the size of the biggest child. They start at the top left corner of its Size (the area the Scene view’s resize handles show, centered on the entity), inside its Padding, with Spacing between them, in the Hierarchy’s order. Their own positions don’t matter: a child spawned into it goes after the last, one destroyed or hidden leaves no gap, and in the Scene view they’re put in place as you add, move or undo them.

// A contract board: the layout places each card, no position needed.
var card = Game.Spawn("OfferCard", board);

A child takes the room of its Button’s clickable area, its Sprite’s Size or its own Layout’s (a list of shelves), whichever is biggest, times its scale; one with only text takes what its letters are guessed to. Set Cell Size to give every child the same room (say 2 by 3 for cards). Put the background sprite on the layout’s own entity: every child showing is placed.

Tick Clip and what doesn’t fit its Size is cut off at its edges, and the mouse wheel over it scrolls it a row at a time (a click or the wheel there isn’t the world’s, as on a button). Scripts read how far it can go in Most Scroll and set Scroll themselves; layout.Arrange() places the children straight away, for a script that wants to know where one it just spawned went (otherwise they’re placed after LateUpdate, before the frame is drawn).

Tick Slide and, while the game runs, its children slide to new places over Slide Time rather than jumping there: the rest close up when one is destroyed or hidden, make room for one spawned in, and glide as the list is scrolled or put in a new order. A child that’s just been added (or shown again) takes its place at once.

Tick Reorderable and the player sets the order of its children: a build queue, a research queue, the batches waiting for a machine. Press on a row and drag it up or down the list (about the grid, for a grid): it follows the mouse (or a finger), drawn over the rest, while the other rows slide out of the way (over Slide Time) to show where it would land, and letting go puts it there. A press that doesn’t move is a click, so a Button on the row still works; Esc, or letting go off the list, puts the row back. Dragged near the end of a layout that clips, the list scrolls. With keys or a controller, pick a row’s button with the arrows and press the layout’s Grab Action (“Interact”: E or Y) to lift it; the arrows then move it, and a second press (or Enter or A) puts it down, Esc or B back where it was.

Each move raises the layout’s Reordered(row, from, to) event, the places counted from 0 among its Rows (the children it shows), and sends its Reorder Message with the row. A script that keeps its own list in the same order moves its item with Layout.MoveIn, and moves a row itself (for ▲ and ▼ buttons) with Move:

var tray = Game.Find("Tray").GetComponent<Layout>();
tray.Reordered += (row, from, to) => Layout.MoveIn(batches, from, to);
tray.Move(walkIn, 0); // to the top: Reordered fires as if the player had dragged it there

Set Save Order As to keep the order in the save data (the rows’ names, separated by commas): it’s saved as the player changes it and put back whenever rows with those names are in the list, such as a Toggles List’s rows made again from its table, or a game loaded. Rows it doesn’t name keep their places, so give each row its own name.

Entity → UI → Bar makes a bar that fills to its Value out of Max, which way its Direction says. Set its Track to an entity’s name (Player, Boss) and it shows that entity’s health with no code; give it an Output too and it shows any of that entity’s outputs (see Outputs), out of what the output says it’s out of (a match board’s Score fills towards its Target Score) or the bar’s own Max: a turn’s Time Left, a faction’s Amount:Gold. When it goes down, the part just lost lingers in the Trail color for a moment before draining away.

A bar can be drawn with your own pictures. Its Fill Image is laid over the whole bar and shown only as far as it’s full, cut off there rather than squeezed, so a gauge’s texture or tick marks stay crisp and in place as it fills (from the left, the right, the bottom or the top, as its Direction says); the trail shows the same picture. Its Background Image is drawn whole behind it, and with a Background Slice (and Background Slice Size) it’s a nine-slice like a sprite’s (see Nine-slice panels), so a frame’s corners keep their shape however long the bar is. Fill, Background and Trail tint the pictures: set them to white to show the pictures as they are. A script’s drawing.Bar(...) takes the same pictures as a BarImages.

A slider takes the same pictures, each optional: its Fill Image is laid over the whole slider and shown only as far as it’s set, cut off rather than squeezed, tinted by Fill; its Background Image is the track, whole or a nine-slice with Background Slice and Background Slice Size, tinted by Background; and its Knob Image replaces the handle, drawn at the end of the filled part in the handle’s usual box and tinted by Knob. Where it can be clicked and dragged doesn’t change, and with no pictures set it is drawn in plain colors as before.

Entity → UI → Stars makes a row of Stars, so many filled: how well a level went. It shows an output (a Level Goal’s Stars), a number in the save data (Saved: the best a level has had, for a level select menu), or its own Value, and with a Delay the stars pop in one after another with its Sound, going on under a pause. See Winning a level.

Entity → UI → Chart makes a line graph for the HUD (pin it with a Screen Anchor) or the world: a factory’s cards a day over the last three days, a town’s population, a price. Its Series are a row each: a name and a color (Cards: #4C9AFF). Each keeps its last Keep points (60 to begin with), oldest on the left: adding one past that drops the oldest, so the chart scrolls. Kind draws a line through the points or a bar for each, the series’ bars side by side.

With no code, a series samples an output: give its row one after = (Cards: #4C9AFF = Cards Today, Gold: #F2BC4B = Amount:Gold; the Inspector offers the outputs the entity has, see Outputs) and set Track to the entity that has it (empty is the chart’s own entity or what it’s inside, * the first in the scene that has it). The chart takes a point as the game starts and another every Sample Every seconds of game time (1 to begin with), so a sped-up game fills it as fast as it plays, and a paused one not at all; one it can’t read yet leaves a gap. A script feeds the rest: chart.Add("Cards", 120) adds a point (chart.Add(120) to the first series; a name no row has makes a series in the next color of Chart.Palette), chart.Set("Cards", values) gives a series all its points, and chart.Clear() (or Clear("Cards")) takes them away.

Its range runs from Min (0 to begin with, lower if a point is below it) up to a round number at or above the highest point (1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6 or 8 times a power of ten, as the Tables panel’s chart does); set Max to fix it, points past it cut off at the top. Lines across mark the bottom, the middle and the top (and 0 when it’s between), each with its number written short (950, 1.2k, 15k, 2.5M) or as Format says (0.0, #,0, 0%). X Labels are words spread along the bottom from the left end to the right (3 days ago, 2 days ago, Yesterday, Today), and Legend shows each series’ name in its color along the top. It’s drawn in its Size (resized in the Scene view like a bar), over its Background, with Grid Color, Text Color, Text Size, Font and Line Width, at its Order.

A chart is cheap to draw: a quad for each point and line, and its words are written again only when its range changes. A rewind puts its points back, and in a network game the host’s points are sent to the other players.

Add a Tooltip to anything (a sprite in the world, a button, a card on the HUD) and resting the mouse on it for its Delay (half a second to begin with, in real time, so it works under a pause menu) shows its Value in a box by the cursor, which goes away as the mouse leaves. Only the topmost one under the mouse shows: what’s pinned to the screen (or a button the mouse is on) before the world, then the highest draw order; one in the world doesn’t show under a button or a system’s HUD, and while a panel is open only those on it do. Moving straight from one tooltip to the next shows the next at once. The part hovered is the entity’s button, else its collider, else its sprite, or the Area you give it. The box is pinned to the screen and drawn over everything: its Size (a line’s height, as a screen-anchored Text’s), Color, Background, Font, Offset from the cursor (it flips to the other side near the screen’s edge) and Width (lines break between words past it). Name a Text in Into (a Hover Info line on the HUD) and it says the tooltip there instead, then goes back to what it said before. Like a Text, it can show an output: Track and Output (Cards), with {0} and {1} in its Value ({0} / {1} cards), read only while it’s the one hovered, so a thousand machines with tooltips cost nothing until one is. An action’s name in braces in its Value shows the key that does it, as in a Text: Key: {OpenWork} reads Key: K, and follows the gamepad and the player’s rebinding. A script sets GetComponent<Tooltip>().Value to say anything else (a deck’s contract, its cards and when it’s due), and reads IsHovered, HoveredFor, IsShowing and Shown.

Entity → UI → Edge Pointer makes an objective marker: an amber arrow with an EdgePointer, which shows the way to its Target (an entity’s name, e.g. Goal) while that’s out of the camera’s view. It sits on the screen’s edge, in by its Margin, where the line from the screen’s middle to the target crosses it, turned to point at the target, and slides along the edge as the camera pans and zooms; once the target is in view it hides. With In View set to Over Target it sits over the target instead (at Over Target from it, (0, 1) over a worker’s head) pointing down at it, so one arrow marks the target on the screen and off it. Its sprite is drawn pointing right. It’s pinned to the screen as a ScreenAnchor is (drawn over the world, keeping its size with the camera’s HudSize and the player’s UI Scale, and a click on it isn’t one in the world), laid out after the anchored HUD each frame, on the main camera’s view. Its Button’s Do is Glide, so clicking it glides the camera there with the camera’s CameraControls and leaves it the player’s (as GlideTo does). The Distance text inside it shows its Distance output (how far the target is from the screen’s middle, in world units) as {0:0}; it sits behind the arrow, so on the screen’s side of it, and stays upright however the arrow turns. Moving the goal on is a Trigger’s or Button’s Point At action, or a script’s pointer.Target = "Reader" (or TargetEntity for an entity it has in hand); with no target, or none of that name, it hides. It decides whether its entity shows, so to hide it some other way, hide what it’s inside. A script reads IsOnEdge and Distance.

A few touches make a game feel alive, with no code, each off until it’s set:

  • Buttons that pop. A Button’s Hover Grow draws it bigger under the mouse (0.08 is 8% bigger), Press Shrink smaller while it’s held (0.06), and Pop Time eases it into each, its shading too (0.1 for a soft pop). Only how it’s drawn changes: its clickable area stays its size.
  • Numbers that count. A Text showing an Output with a Count Time counts up or down to a new number over that many seconds rather than jumping there: a score adding up as points come in, gold draining as it’s spent.
  • Tween Player. Add a TweenPlayer to anything for a little animation of how it’s drawn: grown or shrunk (Scale, times its size), moved (Move), turned (Rotate, in degrees) and faded (Fade, 1 is gone), all at once, over Duration after a Delay, along an Ease (Out Back overshoots and settles, Out Bounce lands like a dropped ball). From (the default) starts at those values and settles to how the entity is, so as it comes it pops in from nothing; To goes the other way and stays there (fading out). It plays as the entity appears (On Start, also each time it’s shown again), when its Message is sent, or from a script (Play(), Stop(), or a Trigger’s Do), once or over and over (Loop: Repeat, or Ping Pong for a bob). What’s inside the entity moves and fades with it, and its transform and collider stay put. A card popping in, a stamp landing (Scale 2, Fade 1, Out Bounce), a score jumping on its message (Scale 1.3), a coin bobbing (Move 0, 0.2, Ping Pong, In Out Sine).
  • Camera kicks. Where a shake rattles the view every way, a kick pushes it one way and eases it back: a Camera’s Kick By says which way and how far (0, -0.3 jolts it down, as a landing does), over Kick Time. A Trigger’s or Button’s Do Kick on the camera kicks it; scripts call Game.KickCamera(direction, seconds).

Add a Pickup to a coin (with a trigger collider) and it’s collected when the player touches it: it sends its Message (Score) with its Amount, plays its Sound, spawns its Effect (a sparkle prefab, in the coin’s color) and disappears. Layers says who can collect it (Player; empty is anything with a body), and Spin turns it while it waits. A Counter on a text adds up its Message by itself, whatever sends it (a pickup, Game.Send("Score", 10), an enemy’s Death Message), and shows it with its Format (Score: {0}, {1} is the best; a counter that counts goals, moves or a match board’s gems has {2} for what it’s out of and {3} for how many are left). With Save Best As (best) it keeps the best score between runs, so a counter on the menu with the format Best: {1} shows it, and with Save As (Coins) it keeps the count itself, so a counter saved as the same name in the next scene (or a minigame) carries on from it (see Carrying inventory, health and counts). One Per Message adds 1 for each message whatever number it carries (a beat clock’s Beat sends the beat’s number, so a counter of beats would otherwise add 0, 1, 2…). With Track and Output it counts any entity’s output instead of a message (a board’s Score, the dungeon’s Floor, a faction’s Amount:Gold; see Outputs), with {2} what it’s out of. Reaches makes it do something at a value: once it’s At Least its Reach (counting up to 10 coins) or At Most it (counting down to no moves left), it sends its Reach Message each time it gets there (a trigger opens a door on it), and its own output Reached is on while it’s there, for a wire into a door’s Power. Starting there (or a rewind putting it back there) isn’t reaching it.

A counter can be multiplied by another: with Multiply By (Multiplier) everything its Message (and its Add action) adds counts that other counter’s count times over, so a pinball score counts double and triple while a multiplier counter says so. Max stops a count going past it (a multiplier up to 5), and its Set action puts it at a value (a trigger setting the multiplier back to 1 as the ball drains). With Save Best As, its New Best output is on once the count beats the best kept as the scene started (a game over panel’s text says “New best!” on it: {0:'New best!';;''}), and it sends its New Best Message the moment it does, with the count (a sound player’s sting, a callout), unless nothing was kept yet. A count changing pops it bigger by its Pop, springing back, without any garbage.

The Arcade template shows all of this: a main menu, options (with a difficulty, a rebindable Fire button and an Audio Offset button that opens a calibration screen to tap along to), a game with a pause menu, and a game over panel with Retry and Menu, fading between them. Its bullets use collision layers to fly out of the ship, and its enemy spawner is a coroutine. On a gamepad it plays twin-stick: move with the left stick, aim with the right, fire with the right trigger. The Platformer and Top-down templates’ coins, gems and scores are pickups and counters, with no scripts.