moxzi
Docs / moxzi.game / Controls

Controlspreview

Define buttons, sliders, keys, taps and on-screen text in controls.ui.yaml, and handle them in Motoko.

The player draws your game's controls and on-screen text from controls.ui.yaml, and handles keyboard, mouse and touch input. Your actor receives named actions in moxzi_input.

The file's metadata must have kind: ui and address: ui/main, or the player won't find it.

assets/controls.ui.yaml
version: 1
title: Drift
labels:
  - id: position
    fallback: "x = 0.0 m"
buttons:
  - id: center
    label: Center
    action: center
    key: space
axes:
  - id: steer
    action: steer
    range: [-5000, 5000]
    initial: 0
    negative_key: a
    positive_key: d
    units_per_second: 4000

Top-level fields#

FieldRequiredMeaning
versionYes1
titleYesShown in the control panel
buttonsYesButtons. Can be empty ([])
axesNoSliders driven by keys or dragging
labelsNoText your actor sets each tick
pointNoAn action sent when the player taps or clicks the world
themeNoPanel colours: background, button, accent, text, each sRGB [r, g, b] from 0 to 1
gameplay_aspectNoWidth-to-height ratio to keep for the game view
helpNoHelp text, up to 4096 bytes

Unknown fields are an error.

Buttons#

FieldMeaning
idUnique id
labelButton text
actionSent to moxzi_input when pressed
payloadOptional text sent with the action
keyOptional keyboard shortcut. See Keys
backgroundOptional button colour, sRGB [r, g, b]
label_fromOptional label id: the button shows that label's current text instead of label

Handle the action in moxzi_input:

src/Main.mo
public shared ({ caller }) func moxzi_input(command : Input.Command) : async Hud.InputAck {
  assert host == ?caller and not suspended;
  assert command.epoch == epoch and command.at_tick == tick;
  assert command.sequence == sequence + 1;
  sequence := command.sequence;

  let accepted = switch (command.action) {
    case "steer" {
      // Axis values arrive as decimal text, such as "-2500".
      switch (parseInt(command.payload)) {
        case (?mm) { target := mm; true };
        case null false;
      }
    };
    case "center" { target := 0; clickPending := true; true };
    case _ false;
  };
  { epoch; sequence; at_tick = tick; accepted; paused = false; output = ""; hud = ?hud() }
};

Player-only buttons#

A button with local instead of action controls the player itself and never reaches your actor:

localEffect
reduced_effectsTurns bloom and shadows down
helpShows the help text
home_viewResets the camera view

Axes#

An axis is a value in a range that keys or dragging change. The player sends the value to your actor as decimal text whenever it changes.

FieldMeaning
id, actionId, and the action sent to moxzi_input
range[min, max] whole numbers
initialStarting value
negative_key, positive_keyKeys that move the value down and up
units_per_secondHow fast a held key changes the value
pointerWhether dragging changes the value. Default true
directionhorizontal (default) or vertical drag
regionPart of the screen where dragging works, [x, y, width, height] as fractions. Default the whole screen
invertReverse the drag direction
planeDrag along a surface in the 3D scene instead of across the screen. See below

Values arrive as UTF-8 text such as "-2500". Parse them as in Drift's parseInt, and reject anything that doesn't parse.

Dragging in the scene#

With plane, the player projects the pointer onto a plane in the world, so the value follows the finger or mouse exactly. Pong uses this for the paddle:

FieldMeaning
originA point on the plane
normalThe plane's normal (unit vector)
axisThe direction along the plane that the value measures (unit vector)
half_extentHalf the size of the draggable area, in metres
units_per_meterValue units per metre

Tapping the world#

point sends an action when the player taps or clicks the scene:

point:
  action: select
  plane:
    origin: [0, 0, 0]
    normal: [0, 1, 0]
    axis: [1, 0, 0]
    half_extent: [50, 50]
    units_per_meter: 1000

The payload is the position on the plane as "U,V" text.

Labels#

labels:
  - id: position
    fallback: "x = 0.0 m"

The player shows fallback until your actor sets the text. Set label text by returning hud = ?[{ id; text }] from moxzi_advance, moxzi_input or moxzi_snapshot. The list replaces all label text: labels you leave out show their fallback again. Return hud = null to keep the current text.

LimitValue
Labels64
Text per label4096 bytes
Total text16384 bytes

Keys#

Keys you can use
arrow_up, arrow_down, arrow_left, arrow_right
w, a, s, d
space, tab
r, v, m

Any other key is rejected when the game is played, with invalid UI YAML: … unknown variant.

Rules for handling input#

Limitations#

LimitationWhat to do
Only the keys above can be boundUse buttons and axes for other inputs
On this pageTop-level fieldsButtonsPlayer-only buttonsAxesDragging in the sceneTapping the worldLabelsKeysRules for handling inputLimitations