Input & Controls¶
All input is polled through the static Input class, typically
from OnUpdate. The scripts that reach for it most
often are data-collection controllers (start and stop recording, mark episode outcomes,
switch tasks) and robot driver scripts (WASD-style locomotion control).
In short
IsKeyDown is reliable in every callback and suits continuous actions such as driving
a robot. IsKeyPressed and IsKeyReleased are true for one rendered frame, while
OnUpdate runs on fixed simulation steps, so for one-shot actions in OnUpdate,
detect the press yourself by comparing IsKeyDown with the previous step. A few editor
shortcuts stay active in Play mode, so avoid those keys for script controls.
Keyboard¶
Keys are identified by the KeyCode enum. Four query styles
cover the common needs. In this table, a frame is a rendered frame:
| Method | Returns true… |
|---|---|
Input.IsKeyPressed(key) |
…on the first frame the key goes down. |
Input.IsKeyHeld(key) |
…every frame after that initial press. |
Input.IsKeyDown(key) |
…every frame the key is down (pressed or held). |
Input.IsKeyReleased(key) |
…on the frame the key is released. |
Input.IsKeyToggledOn(key) reports whether
Caps Lock or Num Lock is on.
Frames and simulation steps¶
OnUpdate and the other per-step callbacks do not run once per rendered frame. They run
on the fixed steps of their time runner, and one
rendered frame can run zero, one or several steps. The pressed and released states last
exactly one rendered frame, so in a per-step callback:
IsKeyPressedcan miss a press, when no step runs during that frame.IsKeyPressedcan report the same press more than once, when several steps run during that frame.
IsKeyDown and IsKeyHeld stay true for as long as the key is held, so they work in any
callback. The same applies to mouse and controller buttons.
Use edge detection for one-shot actions in OnUpdate
Do not rely on IsKeyPressed or IsKeyReleased in OnUpdate for actions that must
happen exactly once per press, such as starting a recording or ending an episode.
Compare IsKeyDown with its value from the previous step instead.
Detecting a press in a per-step callback¶
A small helper turns IsKeyDown into a "pressed this step" signal:
using Hazel;
// Reports a key press once, on the first step the key is down.
public sealed class KeyEdge
{
private readonly KeyCode m_Key;
private bool m_WasDown;
public KeyEdge(KeyCode key) => m_Key = key;
// Call once per step, every step.
public bool Pressed()
{
bool down = Input.IsKeyDown(m_Key);
bool pressed = down && !m_WasDown;
m_WasDown = down;
return pressed;
}
}
A recording controller then uses one KeyEdge per key:
using Hazel;
using Hazel.Data;
public class RecordingControls : Entity
{
private readonly KeyEdge m_Start = new(KeyCode.Enter);
private readonly KeyEdge m_Fail = new(KeyCode.X);
private readonly KeyEdge m_Success = new(KeyCode.Y);
private readonly KeyEdge m_Stop = new(KeyCode.Backspace);
protected override void OnUpdate(float ts)
{
// One-shot recording controls: each press triggers exactly once.
if (m_Start.Pressed())
Observer.StartRecording();
if (m_Fail.Pressed())
Observer.EndCurrentEpisode(success: false);
if (m_Success.Pressed())
Observer.EndCurrentEpisode(success: true);
if (m_Stop.Pressed())
Observer.StopRecording();
}
}
Call Pressed() for every key on every step, before any early return. A key that is
skipped for a few steps can report a stale press when it is next checked.
Alternative: a free-running callback
OnFreePreUpdate runs once per rendered frame, so IsKeyPressed reports each press
exactly once there. It only runs when Enable Free Step Update Scripts is turned on
in Settings → Scene → Scripts. Because it is not tied to the simulation steps,
set a flag there, then handle and clear the flag in OnUpdate. See
Entity Lifecycle.
Continuous actions¶
For actions that should happen on every step while a key is held, such as driving a robot
or holding a gripper closed, use IsKeyDown:
protected override void OnUpdate(float ts)
{
// Continuous robot drive: every step while the key is held.
if (Input.IsKeyDown(KeyCode.W))
DriveForward(ts);
if (Input.IsKeyDown(KeyCode.A))
TurnLeft(ts);
}
IsKeyHeld excludes the first frame of the press. For continuous actions in a per-step
callback, IsKeyDown is the safer choice.
Common bindings¶
There are no engine-defined keybindings for script-level work. These are conventions worth following so users see consistent controls across scenes:
| Key | Common use |
|---|---|
| Enter | Start a recording session |
| X | End the current episode as failed |
| Y | End the current episode as successful |
| Backspace | Stop recording |
| R | Reset the scene to its starting state |
| Space | Pause / step / toggle a debug flag |
| 1 – 9 | Switch task index (with Observer.SetCurrentTaskIndex) |
| W A S D | Drive a robot |
Editor keys in Play mode¶
Some editor shortcuts stay active while the scene is playing. Scripts still receive these keys, but the editor acts on them too, so avoid them for script controls:
| Key | What the editor does in Play mode |
|---|---|
| F5 | Stops Play while the scene runs, unless you are typing in a text field or the Content Browser or File Tree has focus. It doesn't stop a paused scene: click Stop. |
| Alt+P | Stops Play while the scene runs and the Scene viewport or Scene Hierarchy has focus. It doesn't stop a paused scene. |
| Alt+C | Toggles the editor camera. |
| Esc | Returns a hidden or locked cursor to normal. |
| Tab | Toggles Fullscreen while the Scene viewport has focus. |
| Ctrl shortcuts | Most of the editor's Ctrl shortcuts also work in Play mode, for example Ctrl+S (save the scene), Ctrl+O (open a project) and Ctrl+B (build the scripts). Only Ctrl+D, Ctrl+C and Ctrl+V are off. Settings lists them all. |
| V, R, C, U, S, D, Esc | In Sim High Perf. mode, with the pointer over the Scene viewport, these keys press the buttons of the HIGH PERFORMANCE MODE panel: V Disable Viewport Rendering (or Resume Viewport Rendering), R Render Single Frame, C Clear Screen, U UI Responsiveness, S Switch to Realtime, D Open Data Observer and Esc Stop Simulation. |
The Alt shortcuts use the left Alt key.
See Recording with the Observer for the full episode-lifecycle API and Getting Started for the WASD walker example.
Mouse¶
Input exposes the same four query styles for mouse buttons:
IsMouseButtonPressed,
IsMouseButtonHeld, IsMouseButtonDown and IsMouseButtonReleased. Mouse buttons are
identified by the MouseButton enum (Left, Right,
Middle, or Button0 to Button5). The frame-versus-step advice above applies to them
too.
GetMousePosition() returns the cursor
position relative to the editor window, not to the Scene viewport.
Input.SetCursorMode hides or locks the cursor
with CursorMode Hidden or Locked, and Normal
restores it. In Play mode, Esc also returns the cursor to normal.
The scripting API has no mouse-wheel query.
Controllers¶
Connected gamepads and joysticks are queried by ID:
Input.GetConnectedControllerIDs()returns the IDs currently plugged in.IsControllerPresent(id)andGetControllerName(id)check one controller.IsControllerButtonPressed,IsControllerButtonHeld,IsControllerButtonDownandIsControllerButtonReleasedread a button. They take aGamepadButtonvalue or a plain button index.GetControllerAxis(id, axis)returns a stick or trigger position as afloat.GetControllerHat(id, hat)returns the state of a hat (D-pad) as abyte. Axes and hats are plain integer indices.SetControllerDeadzone(id, axis, deadzone)makes an axis read 0 while its value is within the dead zone. The default dead zone is 0.
Axis and button numbering depends on the controller. To find the axis you need, log
GetControllerAxis for each index while you move the stick.
A gamepad driver looks the same as the keyboard one, with the axis value in place of
IsKeyDown:
private const int k_DriveAxis = 1; // check the numbering for your controller
protected override void OnUpdate(float ts)
{
int[] pads = Input.GetConnectedControllerIDs();
if (pads.Length == 0)
return;
float drive = Input.GetControllerAxis(pads[0], k_DriveAxis);
DriveForward(drive * ts);
}
Where to go next¶
- Recording with the Observer: the recording session and episode API these controls call.
- Entity Lifecycle: which callbacks run per step and which run per frame.
- Controlling Robots: turning input into policy commands.
- The
Inputreference lists every method.