input

package module
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 11, 2023 License: MIT Imports: 7 Imported by: 14

README

Ebitengine input library

Build Status PkgGoDev

Overview

A Godot-inspired action input handling system for Ebitengine.

Key features:

  • Actions paradigm instead of the raw input events
  • Configurable keymaps
  • Bind more than one key to a single action
  • Bind keys with modifiers to a single action (like ctrl+c)
  • Simplified multi-input handling (like multiple gamepads)
  • Implements keybind scanning (see remap example)
  • Simplified keymap loading from a file (see configfile example)
  • Implements simulated/virtual input events (see simulateinput example)
  • No extra dependencies (apart from the Ebitengine of course)
  • Solves some issues related to gamepads in browsers
  • Wheel/scroll as action events
  • Motion-style events, like "gamepad stick just moved" (see smooth_movement example)
  • Can be used without extra deps or with gmath integration

This library may require some extra docs, code comments and examples. You can significantly help me by providing those. Pointing out what is currently missing is helpful too!

Some games that were built with this library:

Installation

go get github.com/quasilyte/ebitengine-input

A runnable example is available:

git clone https://github.com/quasilyte/ebitengine-input.git
cd ebitengine-input
go run ./_examples/basic/main.go

Quick Start

package main

import (
	"image"
	"log"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	input "github.com/quasilyte/ebitengine-input"
)

const (
	ActionMoveLeft input.Action = iota
	ActionMoveRight
)

func main() {
	ebiten.SetWindowSize(640, 480)
	if err := ebiten.RunGame(newExampleGame()); err != nil {
		log.Fatal(err)
	}
}

type exampleGame struct {
	p           *player
	inputSystem input.System
}

func newExampleGame() *exampleGame {
	g := &exampleGame{}
	g.inputSystem.Init(input.SystemConfig{
		DevicesEnabled: input.AnyDevice,
	})
	keymap := input.Keymap{
		ActionMoveLeft:  {input.KeyGamepadLeft, input.KeyLeft, input.KeyA},
		ActionMoveRight: {input.KeyGamepadRight, input.KeyRight, input.KeyD},
	}
	g.p = &player{
		input: g.inputSystem.NewHandler(0, keymap),
		pos:   image.Point{X: 96, Y: 96},
	}
	return g
}

func (g *exampleGame) Layout(outsideWidth, outsideHeight int) (int, int) {
	return 640, 480
}

func (g *exampleGame) Draw(screen *ebiten.Image) {
	g.p.Draw(screen)
}

func (g *exampleGame) Update() error {
	g.inputSystem.Update()
	g.p.Update()
	return nil
}

type player struct {
	input *input.Handler
	pos   image.Point
}

func (p *player) Update() {
	if p.input.ActionIsPressed(ActionMoveLeft) {
		p.pos.X -= 4
	}
	if p.input.ActionIsPressed(ActionMoveRight) {
		p.pos.X += 4
	}
}

func (p *player) Draw(screen *ebiten.Image) {
	ebitenutil.DebugPrintAt(screen, "player", p.pos.X, p.pos.Y)
}

Introduction

Let's assume that we have a simple game where you can move a character left or right.

You might end up checking the specific key events in your code like this:

if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    // Move left
}

But there are a few issues here:

  1. This approach doesn't allow a key rebinding for the user
  2. There is no clean way to add a gamepad support without making things messy
  3. And even if you add a gamepad support, how would you handle multiple gamepads?

All of these issues can be solved by our little library. First, we need to declare our abstract actions as enum-like constants:

const (
	ActionUnknown input.Action = iota
	ActionMoveLeft
	ActionMoveRight
)

Then we change the keypress handling code to this:

if h.ActionIsPressed(ActionMoveLeft) {
    // Move left
}

Now, what is h? It's an input.Handler.

The input handler is bound to some keymap and device ID (only useful for the multi-devices setup with multiple gamepads being connected to the computer).

Having a keymap solves the first issue. The keymap associates an input.Action with a list of input.Key. This means that the second issue is resolved too. The third issue is covered by the bound device ID.

So how do we create an input handler? We use a constructor provided by the input.System.

// The ID argument is important for devices like gamepads.
// The input handlers can have the same keymaps.
player1input := inputSystem.NewHandler(0, keymap)
player2input := inputSystem.NewHandler(1, keymap)

The input system is an object that you integrate into your game Update() loop.

func (g *myGame) Update() {
    g.inputSystem.Update() // called every Update()

    // ...rest of the function
}

You usually put this object into the game state. It could be either a global state (which I don't recommend) or a part of the state-like object that you pass through your game explicitely.

type myGame struct {
    inputSystem input.System

    // ...rest of the fields
}

You'll need to call the input.System.Init() once before calling its Update() method. This Init() can be called before Ebitengine game is executed.

func newMyGame() *myGame {
    g := &myGame{}
    g.inputSystem.Init(input.SystemConfig{
		DevicesEnabled: input.AnyDevice,
	})
    // ... rest of the game object initialization
    return g
}

The keymaps are quite straightforward. We're hardcoding the keymap here, but it could be read from the config file.

keymap := input.Keymap{
    ActionMoveLeft:  {input.KeyGamepadLeft, input.KeyLeft, input.KeyA},
    ActionMoveRight: {input.KeyGamepadRight, input.KeyRight, input.KeyD},
}

With the keymap above, when we check for the ActionMoveLeft, it doesn't matter if it was activated by a gamepad left button on a D-pad or by a keyboard left/A key.

Another benefit of this system is that we can get a list of relevant key events that can activate a given action. This is useful when you want to prompt player to press some button.

// If gamepad is connected, show only gamepad-related keys.
// Otherwise show only keyboard-related keys.
inputDeviceMask := input.KeyboardInput
if h.GamepadConnected() {
    inputDeviceMask = input.GamepadInput
}
keyNames := h.ActionKeyNames(ActionMoveLeft, inputDeviceMask)

Since the pattern above is quite common, there is a shorthand for that:

keyNames := h.ActionKeyNames(ActionMoveLeft, h.DefaultInputMask())

If the gamepad is connected, the keyNames will be ["gamepad_left"]. Otherwise it will contain two entries for our example: ["left", "a"].

To build a combined key like ctrl+c, use KeyWithModifier function:

// trigger an action when c is pressed while ctrl is down
input.KeyWithModifier(input.KeyC, input.ModControl)

See an example for a complete source code.

Thread Safety Notice

This library never does any synchronization on its own. It's implied that you don't do a concurrent access to the input devices.

Therefore, keep in mind:

  • Emitting a simulated input event from several goroutines is a data race
  • Using any Handler APIs while System.Update is in process is a data race

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	KeyWheelUp       = Key{/* contains filtered or unexported fields */}
	KeyWheelDown     = Key{/* contains filtered or unexported fields */}
	KeyWheelVertical = Key{/* contains filtered or unexported fields */}
)

Wheel keys.

View Source
var (
	KeyMouseLeft    = Key{/* contains filtered or unexported fields */}
	KeyMouseRight   = Key{/* contains filtered or unexported fields */}
	KeyMouseMiddle  = Key{/* contains filtered or unexported fields */}
	KeyMouseBack    = Key{/* contains filtered or unexported fields */}
	KeyMouseForward = Key{/* contains filtered or unexported fields */}
)

Mouse keys.

View Source
var (
	KeyTouchTap = Key{/* contains filtered or unexported fields */}

	// Like a tap, but user was holding that gesture for at least 0.5s.
	KeyTouchLongTap = Key{/* contains filtered or unexported fields */}

	KeyTouchDrag = Key{/* contains filtered or unexported fields */}
)

Touch keys. Experimental: touch keys API is not stable yet!

View Source
var (
	KeyLeft  = Key{/* contains filtered or unexported fields */}
	KeyRight = Key{/* contains filtered or unexported fields */}
	KeyUp    = Key{/* contains filtered or unexported fields */}
	KeyDown  = Key{/* contains filtered or unexported fields */}

	Key0     = Key{/* contains filtered or unexported fields */}
	Key1     = Key{/* contains filtered or unexported fields */}
	Key2     = Key{/* contains filtered or unexported fields */}
	Key3     = Key{/* contains filtered or unexported fields */}
	Key4     = Key{/* contains filtered or unexported fields */}
	Key5     = Key{/* contains filtered or unexported fields */}
	Key6     = Key{/* contains filtered or unexported fields */}
	Key7     = Key{/* contains filtered or unexported fields */}
	Key8     = Key{/* contains filtered or unexported fields */}
	Key9     = Key{/* contains filtered or unexported fields */}
	KeyMinus = Key{/* contains filtered or unexported fields */}
	KeyEqual = Key{/* contains filtered or unexported fields */}

	KeyQuote     = Key{/* contains filtered or unexported fields */}
	KeyBackquote = Key{/* contains filtered or unexported fields */}

	KeyQ = Key{/* contains filtered or unexported fields */}
	KeyW = Key{/* contains filtered or unexported fields */}
	KeyE = Key{/* contains filtered or unexported fields */}
	KeyR = Key{/* contains filtered or unexported fields */}
	KeyT = Key{/* contains filtered or unexported fields */}
	KeyY = Key{/* contains filtered or unexported fields */}
	KeyU = Key{/* contains filtered or unexported fields */}
	KeyI = Key{/* contains filtered or unexported fields */}
	KeyO = Key{/* contains filtered or unexported fields */}
	KeyP = Key{/* contains filtered or unexported fields */}
	KeyA = Key{/* contains filtered or unexported fields */}
	KeyS = Key{/* contains filtered or unexported fields */}
	KeyD = Key{/* contains filtered or unexported fields */}
	KeyF = Key{/* contains filtered or unexported fields */}
	KeyG = Key{/* contains filtered or unexported fields */}
	KeyH = Key{/* contains filtered or unexported fields */}
	KeyJ = Key{/* contains filtered or unexported fields */}
	KeyK = Key{/* contains filtered or unexported fields */}
	KeyL = Key{/* contains filtered or unexported fields */}
	KeyZ = Key{/* contains filtered or unexported fields */}
	KeyX = Key{/* contains filtered or unexported fields */}
	KeyC = Key{/* contains filtered or unexported fields */}
	KeyV = Key{/* contains filtered or unexported fields */}
	KeyB = Key{/* contains filtered or unexported fields */}
	KeyN = Key{/* contains filtered or unexported fields */}
	KeyM = Key{/* contains filtered or unexported fields */}

	KeyBackslash    = Key{/* contains filtered or unexported fields */}
	KeyBackspace    = Key{/* contains filtered or unexported fields */}
	KeyBracketLeft  = Key{/* contains filtered or unexported fields */}
	KeyBracketRight = Key{/* contains filtered or unexported fields */}
	KeyCapsLock     = Key{/* contains filtered or unexported fields */}
	KeyComma        = Key{/* contains filtered or unexported fields */}
	KeyContextMenu  = Key{/* contains filtered or unexported fields */}
	KeyAlt          = Key{/* contains filtered or unexported fields */}
	KeyAltLeft      = Key{/* contains filtered or unexported fields */}
	KeyAltRight     = Key{/* contains filtered or unexported fields */}
	KeyControl      = Key{/* contains filtered or unexported fields */}
	KeyControlLeft  = Key{/* contains filtered or unexported fields */}
	KeyControlRight = Key{/* contains filtered or unexported fields */}
	KeyDelete       = Key{/* contains filtered or unexported fields */}
	KeyEnd          = Key{/* contains filtered or unexported fields */}
	KeyEnter        = Key{/* contains filtered or unexported fields */}
	KeyEscape       = Key{/* contains filtered or unexported fields */}
	KeyHome         = Key{/* contains filtered or unexported fields */}
	KeyInsert       = Key{/* contains filtered or unexported fields */}
	KeyNumLock      = Key{/* contains filtered or unexported fields */}
	KeyPageDown     = Key{/* contains filtered or unexported fields */}
	KeyPageUp       = Key{/* contains filtered or unexported fields */}
	KeyPause        = Key{/* contains filtered or unexported fields */}
	KeyPeriod       = Key{/* contains filtered or unexported fields */}
	KeyPrintScreen  = Key{/* contains filtered or unexported fields */}
	KeyScrollLock   = Key{/* contains filtered or unexported fields */}
	KeySemicolon    = Key{/* contains filtered or unexported fields */}
	KeyShift        = Key{/* contains filtered or unexported fields */}
	KeyShiftLeft    = Key{/* contains filtered or unexported fields */}
	KeyShiftRight   = Key{/* contains filtered or unexported fields */}
	KeySlash        = Key{/* contains filtered or unexported fields */}
	KeySpace        = Key{/* contains filtered or unexported fields */}
	KeyTab          = Key{/* contains filtered or unexported fields */}

	KeyF1  = Key{/* contains filtered or unexported fields */}
	KeyF2  = Key{/* contains filtered or unexported fields */}
	KeyF3  = Key{/* contains filtered or unexported fields */}
	KeyF4  = Key{/* contains filtered or unexported fields */}
	KeyF5  = Key{/* contains filtered or unexported fields */}
	KeyF6  = Key{/* contains filtered or unexported fields */}
	KeyF7  = Key{/* contains filtered or unexported fields */}
	KeyF8  = Key{/* contains filtered or unexported fields */}
	KeyF9  = Key{/* contains filtered or unexported fields */}
	KeyF10 = Key{/* contains filtered or unexported fields */}
	KeyF11 = Key{/* contains filtered or unexported fields */}
	KeyF12 = Key{/* contains filtered or unexported fields */}

	KeyNum0 = Key{/* contains filtered or unexported fields */}
	KeyNum1 = Key{/* contains filtered or unexported fields */}
	KeyNum2 = Key{/* contains filtered or unexported fields */}
	KeyNum3 = Key{/* contains filtered or unexported fields */}
	KeyNum4 = Key{/* contains filtered or unexported fields */}
	KeyNum5 = Key{/* contains filtered or unexported fields */}
	KeyNum6 = Key{/* contains filtered or unexported fields */}
	KeyNum7 = Key{/* contains filtered or unexported fields */}
	KeyNum8 = Key{/* contains filtered or unexported fields */}
	KeyNum9 = Key{/* contains filtered or unexported fields */}

	KeyNumAdd      = Key{/* contains filtered or unexported fields */}
	KeyNumDivide   = Key{/* contains filtered or unexported fields */}
	KeyNumEnter    = Key{/* contains filtered or unexported fields */}
	KeyNumMultiply = Key{/* contains filtered or unexported fields */}
	KeyNumPeriod   = Key{/* contains filtered or unexported fields */}
	KeyNumSubtract = Key{/* contains filtered or unexported fields */}
)

Keyboard keys.

View Source
var (
	KeyGamepadStart = Key{/* contains filtered or unexported fields */}
	KeyGamepadBack  = Key{/* contains filtered or unexported fields */}
	KeyGamepadHome  = Key{/* contains filtered or unexported fields */}

	KeyGamepadUp    = Key{/* contains filtered or unexported fields */}
	KeyGamepadRight = Key{/* contains filtered or unexported fields */}
	KeyGamepadDown  = Key{/* contains filtered or unexported fields */}
	KeyGamepadLeft  = Key{/* contains filtered or unexported fields */}

	// Stick keys that simulate the D-pad like events.
	KeyGamepadLStickUp    = Key{/* contains filtered or unexported fields */}
	KeyGamepadLStickRight = Key{/* contains filtered or unexported fields */}
	KeyGamepadLStickDown  = Key{/* contains filtered or unexported fields */}
	KeyGamepadLStickLeft  = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStickUp    = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStickRight = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStickDown  = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStickLeft  = Key{/* contains filtered or unexported fields */}

	// Stick button press.
	KeyGamepadLStick = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStick = Key{/* contains filtered or unexported fields */}

	// Stick keys that can be used for the smooth movement.
	KeyGamepadLStickMotion = Key{/* contains filtered or unexported fields */}
	KeyGamepadRStickMotion = Key{/* contains filtered or unexported fields */}

	KeyGamepadA = Key{/* contains filtered or unexported fields */}
	KeyGamepadB = Key{/* contains filtered or unexported fields */}
	KeyGamepadX = Key{/* contains filtered or unexported fields */}
	KeyGamepadY = Key{/* contains filtered or unexported fields */}

	KeyGamepadL1 = Key{/* contains filtered or unexported fields */}
	KeyGamepadL2 = Key{/* contains filtered or unexported fields */}
	KeyGamepadR1 = Key{/* contains filtered or unexported fields */}
	KeyGamepadR2 = Key{/* contains filtered or unexported fields */}
)

Gamepad keys (we're trying to follow the Xbox naming conventions here).

Functions

This section is empty.

Types

type Action

type Action uint32

Action is an ID that represents an abstract action that can be activeted by the input.

type DeviceKind

type DeviceKind uint8

DeviceKind is used as a bit mask to select the enabled input devices. See constants like KeyboardInput and GamepadInput. Combine them like KeyboardInput|GamepadInput to get a bit mask that includes multiple entries. Use AnyDevice if you want to have a mask covering all devices.

const (
	KeyboardDevice DeviceKind = 1 << iota
	GamepadDevice
	MouseDevice
	TouchDevice
)

AnyDevice includes all input devices.

func (DeviceKind) String

func (d DeviceKind) String() string

String returns a pretty-printed representation of the input device mask.

type EventInfo

type EventInfo struct {
	Duration int
	Pos      Vec
	StartPos Vec
	// contains filtered or unexported fields
}

EventInfo holds extra information about the input device event.

Pos carries the event location, if available. Pos is a click location for mouse events. Pos is a tap location for screen touch events. Use HasPos() predicate to know whether there is a pos associated with the event to distinguish between (0, 0) pos and lack of pos info.

StartPos is only set for a few events where it makes sense. A drag event, for instance, will store the "dragging from" location there.

Duration carries the key press duration if available. Duration specifies how long the key has been pressed in ticks same as inpututil.KeyPressDuration. Duration for key press with modifiers it will return the lowest duration of all key presses. Use HasDuration() predicate to know whether there is a duration associated with the event to distinguish between 0 duration and lack of duration info.

func (EventInfo) HasDuration added in v0.9.1

func (e EventInfo) HasDuration() bool

HasDuration reports whether this event has a press duration associated with it. Use Duration field to get the press duration value.

func (EventInfo) HasPos

func (e EventInfo) HasPos() bool

HasPos reports whether this event has a position associated with it. Use Pos field to get the pos value.

func (EventInfo) IsGamepadEvent

func (e EventInfo) IsGamepadEvent() bool

IsGamepadEvent reports whether this event was triggered by a gamepad device.

func (EventInfo) IsKeyboardEvent

func (e EventInfo) IsKeyboardEvent() bool

IsKeyboardEvent reports whether this event was triggered by a keyboard device.

func (EventInfo) IsMouseEvent

func (e EventInfo) IsMouseEvent() bool

IsMouseEvent reports whether this event was triggered by a mouse device.

func (EventInfo) IsTouchEvent

func (e EventInfo) IsTouchEvent() bool

IsTouchEvent reports whether this event was triggered by a screen touch device.

type Handler

type Handler struct {

	// GamepadDeadzone is the magnitude of a controller stick movements
	// the handler can receive before registering it as an input.
	//
	// The default value is 0.055, meaning the slight movements are ignored.
	// A value of 0.5 means about half the axis is ignored.
	//
	// The default value is good for a new/responsive controller.
	// For more worn out controllers or flaky sticks, a higher value may be required.
	//
	// This parameter can be adjusted on the fly, so you're encouraged to
	// give a player a way to configure a deadzone that will fit their controller.
	//
	// Note that this is a per-handler option.
	// Different gamepads/devices can have different deadzone values.
	GamepadDeadzone float64
	// contains filtered or unexported fields
}

Handler is used to associate a keymap with an abstract input consumer.

The ID bound to the handler is used to distinguish which gamepad is related to this handler.

You usually need to create the input handlers only once and carry them through the game using your preferred method.

If any game object needs to handle the input, they need an input handler object.

func (*Handler) ActionIsJustPressed

func (h *Handler) ActionIsJustPressed(action Action) bool

ActionIsJustPressed is like inpututil.IsKeyJustPressed, but operates on the action level and works with any kinds of "keys". It returns true if any of the keys bound to the action was pressed during this frame.

func (*Handler) ActionIsJustReleased added in v0.9.1

func (h *Handler) ActionIsJustReleased(action Action) bool

ActionIsJustReleased is like inpututil.IsKeyJustReleased, but operates on the action level and works with any kinds of "keys". It returns true if any of the keys bound to the action was released during this frame.

Implementation limitation: for now it doesn't work for some of the key types. It's easier to list the supported list:

  • Keyboard events
  • Mouse events
  • Gamepad normal buttons events (doesn't include joystick D-pad emulation events like KeyGamepadLStickUp)

For the keys with modifiers it doesn't require the modifier keys to be released simultaneously with a main key. These modifier keys can be in either "pressed" or "just released" state. This makes the "ctrl+left click just released" event easier to perform on the user's side (try releasing ctrl on the same frame as left click, it's hard!)

TODO: implement other "action released" events if feasible. The touch tap events, for example, doesn't sound useful here: a tap is only registered when the gesture was already finished. Therefore, the tap release event would be identical to a tap activation event. We could re-word this event by saying that "released" happens when previous frame ActionIsPressed reported true and the current frame reported false. But that's a more complicated task. Let's wait until users report their use cases.

Note: this action event is never simulated (see #35).

func (*Handler) ActionIsPressed

func (h *Handler) ActionIsPressed(action Action) bool

ActionIsPressed is like ebiten.IsKeyPressed, but operates on the action level and works with any kinds of "keys". It returns true if any of the keys bound to the action is being pressed.

func (*Handler) ActionKeyNames

func (h *Handler) ActionKeyNames(action Action, mask DeviceKind) []string

ActionKeyNames returns a list of key names associated by this action.

It filters the results by a given input device mask. If you want to include all input device keys, use AnyDevice value.

This function is useful when you want to display a list of keys the player should press in order to activate some action.

The filtering is useful to avoid listing the unrelated options. For example, if player uses the gamepad, it could be weird to show keyboard options listed. For the simple cases, you can use DefaultInputMask() method to get the mask that will try to avoid that situation. See its comment to learn more.

Keys with modifiers will have them listed too. Modifiers are separated by "+". A "k" keyboard key with ctrl modifier will have a "ctrl+k" name.

Note: this function doesn't check whether some input device is available or not. For example, if mask contains a TouchDevice, but touch actions are not available on a machine, touch-related keys will still be returned. It's up to the caller to specify a correct device mask. Using a AnyDevice mask would return all mapped keys for the action.

func (*Handler) CursorPos

func (h *Handler) CursorPos() Vec

CursorPos returns the current mouse cursor position on the screen.

func (*Handler) DefaultInputMask

func (h *Handler) DefaultInputMask() DeviceKind

DefaultInputMask returns the input mask suitable for functions like ActionKeyNames.

If gamepad is connected, it returns GamepadInput mask. Otherwise it returns KeyboardInput+MouseInput mask. This is good enough for the simplest games, but you may to implement this logic inside your game if you need something more complicated.

func (*Handler) EmitEvent

func (h *Handler) EmitEvent(e SimulatedAction)

EmitEvent activates the given action for the player. Only the handlers with the same player ID will discover this action.

Note: simulated events are only visible after the next System.Update() call.

Note: action release events can't be simulated yet (see #35).

See SimulatedAction documentation for more info.

Experimental: this is a part of virtual input API, which is not stable yet.

func (*Handler) EmitKeyEvent added in v0.9.0

func (h *Handler) EmitKeyEvent(e SimulatedKeyEvent)

EmitKeyEvent sends given key event into the input system.

The event is emitted from the perspective of this handler, so the gamepad events will be handled properly in the multi-device context.

Note: simulated events are only visible after the next System.Update() call.

Note: key release events can't be simulated yet (see #35).

See SimulatedKeyEvent documentation for more info.

Experimental: this is a part of virtual input API, which is not stable yet.

func (*Handler) GamepadConnected

func (h *Handler) GamepadConnected() bool

GamepadConnected reports whether the gamepad associated with this handler is connected. The gamepad ID is the handler ID used during the handler creation.

There should be at least one call to the System.Update() before this function can return the correct results.

func (*Handler) JustPressedActionInfo

func (h *Handler) JustPressedActionInfo(action Action) (EventInfo, bool)

JustPressedActionInfo is like ActionIsJustPressed, but with more information.

The first return value will hold the extra event info. The second return value is false if given action is not activated.

See EventInfo comment to learn more.

func (*Handler) JustReleasedActionInfo added in v0.9.1

func (h *Handler) JustReleasedActionInfo(action Action) (EventInfo, bool)

JustReleasedActionInfo is like ActionIsJustReleased, but with more information.

This method has the same limitations as ActionIsJustReleased (see its comments).

The first return value will hold the extra event info. The second return value is false if given action is not just released.

See EventInfo comment to learn more.

Note: this action event is never simulated (see #35).

func (*Handler) PressedActionInfo added in v0.9.0

func (h *Handler) PressedActionInfo(action Action) (EventInfo, bool)

PressedActionInfo is like ActionIsPressed, but with more information.

The first return value will hold the extra event info. The second return value is false if given action is not activated.

See EventInfo comment to learn more.

func (*Handler) Remap added in v0.9.0

func (h *Handler) Remap(keymap Keymap)

Remap changes the handler keymap while keeping all other settings the same.

func (*Handler) TouchEventsEnabled

func (h *Handler) TouchEventsEnabled() bool

TouchEventsEnabled reports whether this handler can receive screen touch events.

type Key

type Key struct {
	// contains filtered or unexported fields
}

Key represents an input method that can be used to activate Action. Key could be a keyboard key, a gamepad key, a mouse button, etc.

Use the predefined global vars like KeyMouseLeft and KeyTab to create a Keymap.

func KeyWithModifier

func KeyWithModifier(k Key, mod KeyModifier) Key

KeyWithModifier turns k into a combined modifier+k key. For instance, KeyUp+ModControl will trigger an action only if both of these keys are being pressed.

func ParseKey

func ParseKey(s string) (Key, error)

ParseKeys tries to construct an appropriate Key object given its name.

It can also be used as a string->key constructor:

ParseKey("left")         // returns KeyLeft
ParseKey("gamepad_left") // returns KeyGamepadLeft

The format is one of the following:

  • keyname
  • mod+keyname
  • mod+mod+keyname

Some valid input examples:

  • "gamepad_left"
  • "left"
  • "ctrl+left"
  • "ctrl+shift+left"
  • "shift+ctrl+left"

See Handler.ActionKeyNames() for more information about the key names.

func (Key) String added in v0.9.0

func (k Key) String() string

type KeyModifier

type KeyModifier uint8
const (
	ModUnknown KeyModifier = iota
	ModControl
	ModShift
	ModControlShift
)

type KeyScanStatus added in v0.9.0

type KeyScanStatus int

KeyScanStatus represents the KeyScanner.Scan operation result.

const (
	KeyScanUnchanged KeyScanStatus = iota
	KeyScanChanged
	KeyScanCompleted
)

type KeyScanner added in v0.9.0

type KeyScanner struct {
	// contains filtered or unexported fields
}

KeyScanner checks the currently pressed keys and buttons and tries to map them to a local Key type that can be used in a Keymap.

Use NewKeyScanner to create a usable object of this type.

Experimental: this is a part of a key remapping API, which is not stable yet.

func NewKeyScanner added in v0.9.0

func NewKeyScanner(h *Handler) *KeyScanner

NewKeyScanner creates a key scanner for the specifier input Handler.

You don't have to create a new scanner for every remap; they can be reused.

It's important to have the correct Handler though: their ID is used to check the appropriate device keys.

Experimental: this is a part of a key remapping API, which is not stable yet.

func (*KeyScanner) Scan added in v0.9.0

func (s *KeyScanner) Scan() (Key, KeyScanStatus)

Scan reads the buttons state and tries to map them to a Key.

It's intended to work with keyboard keys as well as gamepad buttons, but right now it only works for the keyboard.

This function should be called on every frame where you're reading the new keybind combination. See the remap example for more info.

The function can return these result statuses: * Unchanged - nothing updated since the last Scan() operation * Changed - some keys changed, you may want to update the prompt to the user * Completed - the user finished specifying the keys combination, you can use the Key as a new binding

type Keymap

type Keymap map[Action][]Key

Keymap associates a list of keys with an action. Any of the keys from the list can activate the action.

func MergeKeymaps added in v0.9.1

func MergeKeymaps(maps ...Keymap) Keymap

MergeKeymaps merges a list of keymaps into one.

Given maps are not modified. The resulting keymap contains no references to input keymaps.

Any key duplicates for a single action are ignored, therefore:

{Action1: [KeyA, KeyB]} + {Action1: [KeyC, KeyB]} = {Action1: [KeyA, KeyB, KeyC]}

The keys order depends on the input keymaps arguments order. Keymaps with keys of higher priorities should go first. So, if you have a keyboardKeymap and gamepadKeymap keymaps, passing gamepadKeymap before keyboardKeymap would make the gamepad key binds take priority: MergeKeymaps(gamepadKeymap, keyboardKeymap).

func (Keymap) Clone

func (m Keymap) Clone() Keymap

Clone creates a deep copy of a keymap. The returned keymap can be modified without changing the original keymap.

type SimulatedAction added in v0.9.0

type SimulatedAction struct {
	Action Action

	Pos Vec

	StartPos Vec
}

SimulatedAction represents an artificially triggered action.

It shares many properties with SimulatedKeyEvent, but the event consumers will have no way of knowing which input device was used to emit this event, because SimulatedAction has no device associated with it.

As a consequence, all event info methods like IsGamepadeEvent() will report false. It's possible to trigger an action that has no keys associated with it. All actions triggered using this method will be only visible to the handler of the same player ID (like gamepad button events).

Experimental: this is a part of virtual input API, which is not stable yet.

type SimulatedKeyEvent added in v0.9.0

type SimulatedKeyEvent struct {
	Key Key

	Pos Vec
}

SimulatedKeyEvent represents a virtual input that can be send down the stream.

The data carried by this event will be used to construct an EventInfo object.

Experimental: this is a part of virtual input API, which is not stable yet.

type System

type System struct {
	// contains filtered or unexported fields
}

System is the main component of the input library.

You usually need only one input system object.

Store System object (by value) inside your game context/state object like this:

struct GameState {
    InputSystem input.System
}

When ebitengine game is executed, call gameState.InputSystem.Init() once.

On every ebitengine Update() call, use gameState.InputSystem.Update().

The system is usually not used directly after the input handlers are created. Use input handlers to handle the user input.

func (*System) Init

func (sys *System) Init(config SystemConfig)

func (*System) NewHandler

func (sys *System) NewHandler(playerID uint8, keymap Keymap) *Handler

NewHandler creates a handler associated with player/device ID. IDs should start with 0 with a step of 1. So, NewHandler(0, ...) then NewHandler(1, ...).

If you want to configure the handler further, use Handler fields/methods to do that. For example, see Handler.GamepadDeadzone.

func (*System) Update

func (sys *System) Update()

Update reads the input state and updates the information available to all input handlers. Generally, you call this method from your ebiten.Game.Update() method.

Since ebitengine uses a fixed timestep architecture, a time delta of 1.0/60.0 is implied. If you need a control over that, use UpdateWithDelta() instead.

The time delta mostly needed for things like press gesture detection: we need to calculate when a tap becomes a [long] press.

func (*System) UpdateWithDelta added in v0.9.0

func (sys *System) UpdateWithDelta(delta float64)

UpdateWithDelta is like Update(), but it allows you to specify the time delta.

type SystemConfig

type SystemConfig struct {
	// DevicesEnabled selects the input devices that should be handled.
	// For the most cases, AnyDevice value is a good option.
	DevicesEnabled DeviceKind
}

SystemConfig configures the input system. This configuration can't be changed once created.

type Vec

type Vec struct {
	X float64
	Y float64
}

Vec is a simple wrapper around a pair of float64 coordinates.

Since most games use float values for most values, input library converts int pair to the float pair once per Update() call so all usages inside the frame can use already converted values.

We're not using some vector2d library to avoid extra dependencies. It should be easy to convert this Point object into any other structure.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL