Input topic

Input Module

The input module allows you to check for user input from different devices like keyboard, mouse, gamepads and touchscreens.

Bullseye2D polls all information just before the update loop get called.

Note You should only use the input module inside app.onUpdate. In onRender the state is invalid.

Keyboard

You can use Keyboard API to check key states.

To check for a specific Key, you can use the KeyCodes enum.

Function/Property Description
keyboard.peekChar Read a Char from the input queue
keyboard.getChar Return and consume the first Char in the input queue.
keyboard.getCharBuffer Returns a string with the input buffer.
keyboard.keyUp Returns true if the key was released in the current frame.
keyboard.keyHit Returns true if the key was hit in the current frame.
keyboard.keyDown Returns true if the key is currently pressed down

Here is a short example how to read the keyboard:

import 'package:bullseye2d/bullseye2d.dart';

class KeyboardDemo extends App {
  late BitmapFont font;

  @override
  onCreate() async {
    font = resources.loadFont("fonts/roboto/Roboto-Regular.ttf", 32);
  }

  @override
  onUpdate() {
    // get first char from the chard queue
    // Returns Char.empty if index is not found in queue
    Char char = keyboard.peekChar(0);
    if (char != Char.empty) {
      log("Char hit: ", char.symbol);
    }

    // Reads and consumes the next char in the input queue.
    char = keyboard.getChar();

    if (keyboard.keyUp(KeyCodes.W)) log("Ley 'W' was released!");

    // Use keyboard.keyHitCountSinceLastFrame(KeyCodes.W) if you want to 
    // know if 'W' was hit multiple times
    // since the last update
    if (keyboard.keyHit(KeyCodes.W)) log("Key 'W' is hit!");

    if (keyboard.keyDown(KeyCodes.W)) log("Key 'W' is currently pressed down!");
  }

  @override onRender() {
    gfx.clear(0, 0, 0, 1);
    gfx.drawText(font, "Press 'W' on your keyboard, watch the developer console ;)", 
      x: 25, 
      y: 25
    );
  }
}

main() {
  KeyboardDemo();
}

Mouse

The Mouse API handles mouse button presses, movement, wheel, and touch input. Coordinates are relative to the canvas (by default).

Property/Method Description
mouse.x Current mouse cursor X coordinate within the canvas.
mouse.y Current mouse cursor Y coordinate within the canvas.
mouse.mouseDown(MouseButton) true if the specified mouse button is currently held down.
mouse.mouseUp(MouseButton) true if the specified mouse button was released this frame.
mouse.mouseHit(MouseButton) true if the specified mouse button was pressed down this frame.
mouse.mouseZ Mouse wheel scroll delta for the current frame. Positive for scroll down/away, negative for scroll up/towards.

Example:

import 'package:bullseye2d/bullseye2d.dart';

class MouseDemo extends App {
  late BitmapFont font;

  @override
  onCreate() async {
    font = resources.loadFont("fonts/roboto/Roboto-Regular.ttf", 32);

    // set update Rate to display refresh rate
    updateRate = 0;
  }

  @override
  onUpdate() {
    double ty = 25;

    gfx.clear(0, 0, 0, 1);

    // Wait until app retrieved the first mouse coordinate
    if (mouse.x == -double.maxFinite) {
      gfx.drawText(font, "Please move your mouse over the canvas", x: 25, y: ty);
      return;
    }

    int mouseX = mouse.x.toInt();
    int mouseY = mouse.y.toInt();

    gfx.drawText(font, "Mouse Position $mouseX, $mouseY", x: 25, y: ty); ty += 30;

    if (mouse.mouseDown(MouseButton.Left)) {
      gfx.drawText(font, "Left Mouse Button was pressed", x: 25, y: ty); ty += 30;
    }

    if (mouse.mouseDown(MouseButton.Middle)) {
      gfx.drawText(font, "Middel Mouse Button was pressed", x: 25, y: ty); ty += 30;
    }

    if (mouse.mouseDown(MouseButton.Right)) {
      gfx.drawText(font, "Right Mouse Button was pressed", x: 25, y: ty); ty += 30;
    }
  }
}

main() {
  MouseDemo();
}

Gamepad

The Gamepad API supports up to 4 gamepads with a standard (XBOX Controller) mapping.

Function / Property Description
gamepad.countDevices() Returns the number of connected gamepads.
Button States
gamepad.joyDown(port, button) Returns true if the specified button (GamepadButton enum, e.g., A, LB, Start, Up) on the gamepad at port (index 0-3) is currently held down.
gamepad.joyUp(port, button) Returns true if the specified button (GamepadButton enum) on the gamepad at port (index 0-3) was released this frame.
gamepad.joyHit(port, button) Returns true if the specified button (GamepadButton enum) on the gamepad at port (index 0-3) was pressed down this frame.
Axes (Joysticks)
gamepad.joyX(port, stick) Returns the X-axis value (-1.0 to 1.0) for the specified stick (Joystick enum: Left, Right) on the gamepad at port (index 0-3).
gamepad.joyY(port, stick) Returns the Y-axis value (-1.0 to 1.0) for the specified stick (Joystick enum: Left, Right) on the gamepad at port (index 0-3).
Triggers
gamepad.joyZ(port, trigger) Returns the trigger value (0.0 to 1.0) for the specified trigger (Trigger enum: Left, Right) on the gamepad at port (index 0-3).

Example:

if (gamepad.countDevices() > 0) {
  if (gamepad.joyHit(0, GamepadButton.A)) {
    log("Gamepad 0: Button A pressed!");
  }
  double leftStickX = gamepad.joyX(0, Joystick.Left);
  if (leftStickX.abs() > 0.1) { // Apply a deadzone
    log("Gamepad 0: Left stick X = $leftStickX");
  }
}

Note To detect gamepads the user needs to first press any buttons on them. This is usally required by the browser to connect the game device to your context.

Accelerometer

The Accelerometer API provides device motion data. Values are normalized (1.0 typically means 1G of acceleration).

Property Description
accel.x Acceleration along the X-axis.
accel.y Acceleration along the Y-axis.
accel.z Acceleration along the Z-axis.

The orientation of these axes depends on the device's screen orientation.

Note On iOS you can only retrieve the accelerometer values if you first prompt the user for permission.

If you want to automatcially prompt the user for permission on the first interaction (first click), then set autoRequestAccelerometerPermission to true in the app config.

import 'package:bullseye2d/bullseye2d.dart';

class AccelDemo extends App {
  AccelDemo([super.config]);
  
  late BitmapFont font;

  @override
  onCreate() async {
    font = resources.loadFont("fonts/roboto/Roboto-Regular.ttf", 128);

    // set update Rate to display refresh rate
    updateRate = 0;
  }

  @override
  onUpdate() {
    gfx.clear(0, 0, 0, 1);
    gfx.drawText(font, "X: ${accel.x.toStringAsFixed(2)}", x: 25, y: 25);
    gfx.drawText(font, "Y: ${accel.y.toStringAsFixed(2)}", x: 25, y: 225);
    gfx.drawText(font, "Z: ${accel.z.toStringAsFixed(2)}", x: 25, y: 425);
  }
}

main() {
  // On iOS Devices it is requried to request permission to read out the
  // gyroscope, so we configure the app to handle it automatically for us
  // on the first user interaction
  AccelDemo(
    AppConfig(autoRequestAccelerometerPermission: true)
  );
}

Classes

Accelerometer Input
Provides access to the device's accelerometer data, including the force of gravity.
Char Input
Represents a single character input event, including the character symbol and the physical key code that produced it.
Gamepad Input
Manages input from connected gamepads.
Keyboard Input
Manages keyboard input, tracking key states (down, up, hit) and buffering character input.
Mouse Input
Manages mouse and touch input.

Enums

GamepadButton Input
Represents the standard buttons and directional inputs found on a typical gamepad.
Joystick Input
Identifies the left or right analog stick on a gamepad.
KeyCodes Input
Represents physical key codes
MouseButton Input
Represents the buttons on a mouse.
Trigger Input
Identifies the left or right analog trigger on a gamepad.