Input and controls
Open Settings → Project Input (or use Input in the bottom shelf) to connect keys, mouse buttons and gamepads to named actions in scripts and Flow. Built-in controllers also expose their own movement and jump bindings in the Inspector.
Create an action for a script
- Open Input → Gameplay. This context is active by default.
- Click Jump · Space, Move left / right · A D, or Move 2D · WASD for a ready-to-use action. Add Action creates a custom one.
- Choose Button for jump/fire, Axis for left/right, or Vector2 for four-direction movement. Click + to add the appropriate binding. For movement, Left stick adds a gamepad stick; its selector can switch to the right stick.
- Copy the input expression displayed under the action. A string matches its stable ID or a unique, case-sensitive name. Renaming the action requires updating string references; typed
Actions.Jumpbindings keep their saved identity. - Press Play, focus the game, and test. The row's meter shows resolved action activity. The device map separately shows which physical inputs are arriving.
| Action | Script read | Result |
|---|---|---|
| Button named Jump | Input.Pressed("Jump") | True once on the press frame |
| Button named Jump | Input.Held("Jump") | True while held |
| Button named Jump | Input.Released("Jump") | True once on release |
| Axis named MoveHorizontal | Input.Axis("MoveHorizontal") | -1 left, 0 idle, +1 right |
| Vector2 named Move | Input.Vector("Move") | X/Y direction; diagonals are normalized |
Input.Pressed("Space") only works if an action is named Space. It does not read a raw key. Missing actions and inactive contexts read false or zero. Additional contexts require the Push input context Flow action; choosing their tab in the panel only edits them.
For a complete input-to-physics example, see Jump with Space. The full Input API is also available in Scripts → Manual → Reference → Input.
Pointer interactions
A pointer is a mouse cursor or a touch point. Both produce the same interactions.
| You want | Add this | Reacts to |
|---|---|---|
| An object the player can tap | Interactable component (or an on-tap graph) | Tap, double-tap, long-press |
| An object the player can drag | Draggable component | Press, drag, release |
| A place a dragged object can be dropped | Drop Zone component | An accepted drop |
In Visual scripting, objects can also listen for pointer events directly — tap, double tap, long press, pointer enter / pointer leave, and the drag lifecycle (drag start, drag, drag end) plus drop results (drop, dropped on). Use these when a plain component reaction is not enough.
A tap only reaches an object that opts in. Decorations and background art are ignored, so taps fall through to the interactive object beneath them.
Do — tappable coin
Interactableenabled, Block taps behind `on` (default — consumes the tap)- Sprite 48×48 px with 8 px padding in Asset Studio
On Tap → Add Counter score +1 → Destroyon the coin itself
Don't — wall that eats taps
- Big invisible panel with
Interactable+Block taps behind `on`sitting above the coin - Transparent hitbox twice the sprite size, not trimmed
On Tapon a parent that never receives the tap
Multi-touch
Opal games are multi-touch. Every finger on the screen is tracked as its own independent pointer, so more than one thing can happen at once:
- Two objects, two fingers. A player can drag two draggable objects at the same time — each object follows only its own finger.
- Drag while tapping. One finger can hold and drag an object while another finger taps or interacts with a different object.
- Independent gestures. Each touch has its own tap, drag, and long-press timing — one finger's gesture never cancels another's.
There is nothing to turn on. Any Interactable or Draggable object automatically works under multiple simultaneous touches, and this holds both in editor Play mode and in published and exported games.
Two-finger interaction is easiest to feel on a real phone or tablet. Desktop browsers have one mouse pointer, so you will see one interaction at a time there.
Keyboard controls
The keyboard is the main way to drive character movement.
When a movement component (Top-Down, Platformer, or Side-Scroller Controller) is set to Controlled By: Player, it reads the keys configured on that component:
| Field | Default | Moves |
|---|---|---|
| Left Key | ArrowLeft | Left |
| Right Key | ArrowRight | Right |
| Up Key | ArrowUp | Up |
| Down Key | ArrowDown | Down |
| Jump Key | Space | Jump (Platformer) |
WASD Also Moves is on by default, so W A S D work alongside the arrow keys. In a Platformer, the Up key and W also jump, so a character can jump without any extra setup. Set Controlled By to Flow, AI, or Network when movement should come from somewhere other than the keyboard.
For custom key handling anywhere in a game, Flow graphs can listen for key down and key up events and act on any key.
Playing on touch devices
Because Opal uses one shared input path for mouse and touch, tap and drag gameplay works on phones and tablets out of the box — including multi-touch. Games built around tapping objects, dragging pieces, and dropping them onto targets are ready for mobile with no extra work.
The touch joystick gap — three honest workarounds
Player character controllers read the keyboard by default, and the editor does not yet ship a placeable on-screen virtual joystick / button pack. At runtime the controller also reads a virtualInput store ({ moveX, moveY, jump }, additive with keys), but there is no Set virtualInput Flow node — you drive movement through controller actions (Move Direction, Move Toward, Jump) with Controlled By: Flow. For a touch-first game today, use one of these:
Set controller Controlled By: Flow. On your Scene Flow:
On Tap (on the ground object) → Move To → target = pointer.worldX/Y
On Tap (on the ground) → Play Motion "Walk"
On Timer 0.15s → Stop Motion when arrivedNo virtualInput needed. Works on phone and desktop. Feels like an adventure game.
Keep controller Controlled By: Player. Add 4 UI Button widgets (← → ↑ ↓) anchored bottom-left. Each button emits a custom event you listen for in Flow:
UI Button "Left" → On Custom Event "leftDown" → Move Direction x:-1 y:0 (on player, once; hold repeats via repeat)
UI Button "Left" → On Custom Event "leftUp" → Move Direction x:0 y:0 (or Stop)
# mirror for Right / Up / Down
UI Button "Jump" → On Custom Event "jumpDown" → Jump (Platformer action)Use Move Direction (x, y in -1..1) on a controller set to Controlled By: Flow or Move Toward (x, y, stopWithin). The runtime also keeps a virtualInput store ({ moveX, moveY, jump }) that is additive with keys, but there is no Set virtualInput node — drive movement through those Flow actions. Make buttons 44×44 px minimum with 8 px padding — that's a comfortable finger target.
Set controller Controlled By: Flow and drive it entirely from Flow graphs. On your player Object Flow:
On Custom Event "leftDown" → Move Direction x:-1 y:0 (on player)
On Custom Event "leftUp" → Move Direction x:0 y:0
On Custom Event "rightDown" → Move Direction x:1 y:0
On Custom Event "rightUp" → Move Direction x:0 y:0
On Custom Event "jumpDown" → Jump
# Hold a direction → player walks; release → Stop. Combine with keyboard — Flow intent is additive with keys.For point-and-click movement, use Move Toward (x, y, stopWithin: 8) to a tapped world position instead of holding a direction. This is the "you own every frame" path — use it when you need coyote time, buffered jumps, or an analog-ish stick you draw yourself.
Touch-friendly checklist
- [ ] Tap targets ≥ 44×44 px including padding (mouse targets can be 24 px — fingers can't)
- [ ] HUD elements anchored to corners/edges so they stay readable at 375 px and 1280 px
- [ ] Tested in fullscreen Play with browser chrome visible (iOS Safari hides the bottom bar and shrinks the viewport)
- [ ] Nothing large with
Interactable(Block taps behindon) sits above smaller tap targets - [ ] Dragged objects have a Drop Zone with a visible highlight when something hovers
Debugging input
| Symptom | Check |
|---|---|
| Tap never fires | Interactable disabled, wrong object, edit mode not Play, covered by another Interactable |
| Drag snaps back | Draggable missing, or Drop Zone rejected the drop — watch Flow overlay for drop vs droppedOn |
| Keys move the wrong object | Two controllers both set to Player — set the non-player one to Flow or AI |
| Virtual buttons drift | Move Direction left non-zero on pointer up — always send x:0 y:0 / Stop on pointer up / pointer leave |
| Multi-touch fails on desktop | Expected — desktop has one mouse. Test on a phone with Play → Fullscreen |
Related docs
- Input — OpalScript class for named project actions
- Components and properties
- Visual scripting
- UI and HUD
- Physics and collisions
- Testing and debugging