Inputs

Inputs

This docs page is under construction. Expect more information coming to this page soon!
If you have questions, please ask them in our Discord server and we'll answer them promptly! 😊

Dreamlab offers a flexible system for responding to player input, providing a simple way to listen to incoming input events and automatically adds your game’s inputs to our input binding UI.

Registering Inputs

Inputs can be referenced either by Input Code, which corresponds to keyboard or mouse inputs, or by registering a named input with the input system. The advantage of named inputs is that they are automatically picked up by Dreamlab’s UI and allows players to set custom key binds.

💡

View a full list of available input codes here.

Registering an input should be done as early as possible, usually this is done inside of an entities init() function. Since the input system is only available on the client, you need to check that it is available first.

Registering an input requires a unique identifier, a human readable label, and a default keybind. The human readable name will be used for the automatic rebinding UI. As with Spawnable Entities, we recommend namespacing input identifiers using a similar @entity/input format, but again this is just a convention.

We recommend using a string enum to define input identifiers in a type-safe way.

TypeScript
import type { SpawnableContext } from '@dreamlab.gg/core'
import { SpawnableEntity } from '@dreamlab.gg/core'
import { inputs } from '@dreamlab.gg/core/labs'
 
// Define inputs with a string enum
enum ExampleInput {
  MyInput = '@example/my-input',
  MyOtherInput = '@example/my-other-input',
}
 
type Args = typeof ArgsSchema
const ArgsSchema = z.object({})
 
export class ExampleInputs extends SpawnableEntity<Args> {
  public constructor(ctx: SpawnableContext<Args>) {
    super(ctx)
 
    // Use a "magic" function to get a reference to the input manager
    const $inputs = inputs()
    if ($inputs) {
      // ensure you only do input related code on the client
 
      $inputs.registerInput(ExampleInput.MyInput, 'My Input', 'KeyG')
      $inputs.registerInput(ExampleInput.MyOtherInput, 'Other Input', 'KeyH')
 
      // ... register more inputs
    }
  }
}

Responding to Inputs

You can either poll the state of an input directly, or attatch an event listener to the input system to run code when an input state changes. Usually you should use event listeners, unless you want to run code that deals with physics bodies.

Event Based

Input events are fired when the input pressed state changes, and this state is passed in to event handlers as a boolean argument. Make sure you only respond to the event for one state or your code will execute twice, once when the input is pressed and once when gets released.

TypeScript
// ... clipped
 
export class ExampleInputs extends SpawnableEntity<Args> {
  public constructor(ctx: SpawnableContext<Args>) {
    const $inputs = inputs()
    if ($inputs) {
      $inputs.registerInput(ExampleInput.MyInput, 'My Input', 'KeyG')
 
      // Listen to keys directly
      $inputs.addListener('Space', (pressed: boolean) => {
        // Only run when the input has been pressed
        if (!pressed) return
 
        // ...
      })
 
      // Listen to named inputs
      // Inputs must be registered first
      $inputs.addListener(ExampleInput.MyInput, (pressed: boolean) => {
        // ...
      })
    }
 
    return { game }
  }
 
  // ... clipped
}

Tick Based

TypeScript
// ... clipped
 
export class ExampleInputs extends SpawnableEntity<Args> {
  // ... clipped
 
  public override onPhysicsStep({ time, delta }, { game }) {
    const inputs = game.client?.inputs
    if (inputs) {
      // Get the state of a raw input
      const keyPressed = inputs.getKey('Space')
 
      // Get the state of a named input
      const inputPressed = inputs.getInput(ExampleInput.MyInput)
 
      // ...
    }
  }
 
  // ... clipped
}

Mouse Inputs