Input
The console has a D-pad, A, B, Select, Cancel and Home. A game sees the first eight; Home always returns to the console's menu and never reaches 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 | none |
| Home | Ctrl+Q | Home, or Select |
A gamepad's Select leaves the game, as Home does, and its Start is the game's menu. A gamepad has no Cancel, so a menu should close with Select (Start on the pad) or B as well.
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.