Input
The console has a D-pad, A, B, Select, Cancel and Home. A game sees the first eight; Home is the console's and never reaches the game: pressed, it opens the console's overlay (clock, friends, chat) while the game keeps running; held for a second, it leaves the game.
| Button | Name in Lua and scenarios | Meant for |
|---|---|---|
| D-pad | "up", "down", "left", "right" | moving, choosing |
| A | "a" | the main action, confirm |
| B | "b" | the second action (jump, run, shoot) |
| Select | "select" | the game's menu, pause |
| Cancel | "cancel" | back, close a menu |
Reading buttons
| Function | True when |
|---|---|
input.down(b) | the button is held this tick |
input.pressed(b) | it went down this tick |
input.released(b) | it went up this tick |
input.dpad() | returns x, y: x is −1 (left), 0 or 1 (right), y is −1 (down), 0 or 1 (up) |
input.repeated(b [, delay [, rate]]) | it went down this tick, then again every rate seconds (0.1) once held for delay (0.3): a cursor |
input.held(b) | returns how long it has been held, in seconds (0 when up): a charge |
kinds.ship = {
update = function(e)
local dx, dy = input.dpad()
e:move(dx * 6 * engine.dt, dy * 6 * engine.dt, 0)
if input.pressed("a") then
scene.spawn{kind = "shot", model = "quad", material = "shot",
position = {e.x, e.y + 0.5, 1}, scale = {0.2, 0.4, 1}, tags = {"shot"}}
end
e.state.charging = input.down("b")
end,
}
Use pressed for actions that happen once per press (jump, shoot, confirm) and down for
actions that last while the button is held (run, charge). Opposite D-pad directions held
together cancel out; a diagonal is (±1, ±1), so normalise it if diagonal movement must not
be faster.
kinds.archer = {
update = function(e)
if input.released("b") then
trace("shot", {power = e.state.charge}) -- the charge of the last tick held
end
e.state.charge = math.min(input.held("b"), 1) -- 0 to 1 over a second held
end,
}
Input is read once per tick. A button pressed and released within one tick still shows in
pressed and released, but not in down.
On the PC
The simulator maps the console's buttons to the keyboard, and any gamepad works too:
| Console | Keyboard | Gamepad |
|---|---|---|
| D-pad | arrows, or W A S D | D-pad (or the stick, on a pad whose D-pad is not four buttons) |
| A | Space or Z | A |
| B | X or Shift | B |
| Select | Enter or Tab | Start |
| Cancel | Escape or Backspace | Select |
| Home | Home (Ctrl+Q leaves at once) | Home, or Select and Start together |
A gamepad's Start is the game's menu and its Select is Cancel; a pad with no Home button has Home on Select and Start pressed together. In the simulator there is no overlay, so Home leaves the game.
Menus
ui.menu is a menu that reads the buttons itself: up and down move the cursor (repeating
when held, through input.repeated), skipping disabled items, A chooses, B or Cancel
cancels.
local menu = ui.menu{"RESUME", "RESTART", "QUIT", title = "PAUSED"}
screen.add("pause", {
update = function()
local i, item = menu:update()
if i == 1 or item == "cancel" or input.pressed("select") then
screen.pop()
elseif i == 2 then
screen.pop()
scene.load(scene.name())
elseif i == 3 then
screen.go("title")
end
end,
draw = function() menu:draw() end,
})
function game.update()
if input.pressed("select") then screen.push("pause") end
end
A screen pushed over the game stops its kinds, bodies and timers until it is popped: the game pauses behind the menu without a check in every kind. See Game Toolkit.
Dialogs take the buttons
While a ui.dialog is open, while scene.load(name, {fade = ...}) fades, and on the tick a
dialog closes, every input function reads as nothing (false, 0): A that closes a dialog
does not also make the hero jump. ui.busy() tells when this is so.
In tests
Scenarios press and release the same button names at given ticks (see Testing). Home cannot be pressed by a scenario, since no game ever sees it.