Core Concepts /Handling Input

Core Concepts

Handling Input

Keyboard, mouse and touch. When to poll input every frame, when to react to events, and how to support phones.

There are two ways to read input, and most games use both:

  • Polling asks "is this held right now?" inside export function update()Screen.OnUpdate()def update(). Use it for continuous things like movement, aiming and holding to charge.
  • Events say "this just happened" once per press. Use them for one-off actions like jumping, shooting, menus and toggles.
You want to… Use
Move while an arrow key is held input.isKeyDown()Input.IsKeyDown()isKeyDown() in export function update()Screen.OnUpdate()def update()
Jump once when Space is pressed export function onKeyDown()Input.OnKeyDown()def keyDown()
Paint while the mouse button is held input.isMouseDown()Input.IsMouseDown()isMouseDown() in export function update()Screen.OnUpdate()def update()
Place a block on click export function onMouseDown()Input.OnMouseDown()def mouseDown()
Follow a finger input.touchX()Input.TouchX()touchX() / input.touchY()Input.TouchY()touchY() in export function update()Screen.OnUpdate()def update()
React when a finger lands export function onTouchStart()Input.OnTouchStart()def touchStart()

#Keyboard

Keys are named like the browser's KeyboardEvent.key: "a", "A" (with Shift), "ArrowLeft", " " (Space), "Enter", "Shift", "Escape".

import * as input from "codetoy/input"
import { deltaTime } from "codetoy/time"

let x: f64 = 200
let jumping = false

export function update(): void {
  // Polling: smooth movement while held
  const step = 250 * deltaTime()
  if (input.isKeyDown("a") || input.isKeyDown("ArrowLeft"))  x -= step
  if (input.isKeyDown("d") || input.isKeyDown("ArrowRight")) x += step
}

// Event: fires once per press
export function onKeyDown(key: string): void {
  if (key == " ") jumping = true
}
double x = 200;
bool jumping = false;

Screen.OnUpdate(() => {
    // Polling: smooth movement while held
    double step = 250 * Screen.DeltaTime;
    if (Input.IsKeyDown("a") || Input.IsKeyDown("ArrowLeft"))  x -= step;
    if (Input.IsKeyDown("d") || Input.IsKeyDown("ArrowRight")) x += step;
});

// Event: fires once per press
Input.OnKeyDown((string key) => {
    if (key == " ") jumping = true;
});
from codetoy._input import isKeyDown
from codetoy.time import deltaTime

x = 200.0
jumping = False

def update():
    global x
    # Polling: smooth movement while held
    step = 250 * deltaTime()
    if isKeyDown("a") or isKeyDown("ArrowLeft"):  x -= step
    if isKeyDown("d") or isKeyDown("ArrowRight"): x += step

# Event: fires once per press
def keyDown(key):
    global jumping
    if key == " ":
        jumping = True

Letter case matters

With Shift or Caps Lock on, pressing A reports "A", not "a". Check both if that matters to your game.

#Mouse

input.mouseX()Input.MouseXmouseX() and input.mouseY()Input.MouseYmouseY() are the pointer's position in canvas pixels. Buttons are numbered 0 (left), 1 (middle) and 2 (right).

export function update(): void {
  // Hold the left button to paint
  if (input.isMouseDown(0)) {
    fill(255, 120, 60)
    circle(input.mouseX(), input.mouseY(), 12)
  }
}

export function onMouseDown(button: i32): void {
  console.log("Clicked button " + button.toString())
}
Screen.OnUpdate(() => {
    // Hold the left button to paint
    if (Input.IsMouseDown(0)) {
        Canvas.Fill(255, 120, 60);
        Canvas.Circle(Input.MouseX, Input.MouseY, 12);
    }
});

Input.OnMouseDown((int button) => {
    Console.Log($"Clicked button {button}");
});
from codetoy._input import isMouseDown, mouseX, mouseY

def update():
    # Hold the left button to paint
    if isMouseDown(0):
        fill(255, 120, 60)
        circle(mouseX(), mouseY(), 12)

def mouseDown(button):
    print(f"Clicked button {button}")

#Touch

On phones and tablets every finger gets an id that stays the same until it lifts. Touch events give you the id and position. Use input.touchCount()Input.TouchCount()touchCount(), input.isTouchDown()Input.IsTouchDown()isTouchDown(), input.touchX()Input.TouchX()touchX() and input.touchY()Input.TouchY()touchY() to poll.

export function onTouchStart(id: i32, x: f64, y: f64): void {
  console.log("Finger " + id.toString() + " down")
}

export function update(): void {
  if (input.isTouchDown()) {          // any finger down?
    fill(120, 255, 160)
    // Draw under the first finger that landed
    // (use the ids from onTouchStart to track several)
  }
}
Input.OnTouchStart((int id, double x, double y) => {
    Console.Log($"Finger {id} down at {x}, {y}");
});

Screen.OnUpdate(() => {
    if (Input.IsTouchDown()) {          // any finger down?
        Canvas.Fill(120, 255, 160);
    }
});
fingers = {}

def touchStart(id, x, y):
    fingers[id] = True

def touchEnd(id):
    fingers.pop(id, None)

def update():
    for id in fingers:
        if isTouchDown(id):
            fill(120, 255, 160)
            circle(touchX(id), touchY(id), 30)

input.touchX()Input.TouchX()touchX() and input.touchY()Input.TouchY()touchY() return -1 for an id that isn't down. There's a full multi-touch example on the Examples page.

#Supporting mouse and touch

To keep a game playable everywhere, treat a tap like a click. Handle both export function onMouseDown()Input.OnMouseDown()def mouseDown() and export function onTouchStart()Input.OnTouchStart()def touchStart() and call the same function from each. Also keep important buttons large, since fingers are less precise than a cursor.

Keyboard focus

Keyboard events only reach your codetoy after the preview has focus. If keys don't respond, click the canvas once. Embeds and Discord work the same way.

Esc