Input Polling / Input Controller
One of the basic features a game needs is the control of inputs. And it is not done with just checking which key is pressed or where the mouse is. Most games need much more than just reacting on clicks.
Imagine a combat game, where you allow users to have key-combos for special attacks and animations.
Therefor we provide the easy to use TGorillaInputController component to access keyboard, mouse and gamepad feedbacks. Also in combination!
Input Devices
The input controller sets up handlers for keyboard, mouse and gamepad, and enables all by default.
FInput.Supported := [TGorillaInputDeviceType.Keyboard, TGorillaInputDeviceType.Mouse, TGorillaInputDeviceType.GamePad];
If you do not need any of the device, you can disable them, by reducing the “Supported” enumeration set. For example, if you only want to check for gamepad events:
FInput.Supported := [TGorillaInputDeviceType.GamePad];
HotKey
A HotKey is a combination of simultanously active inputs. Each HotKey can manage inputs from keyboard, mouse and/or gamepad. An input is a hardware message:
- For keyboards, inputs are the pressed keys
- For mouse, inputs are pressed mouse buttons
- For gamepad, inputs are pressed gamepad buttons
You can define up to 4 inputs per device. Which means you can handle up to 12 simultanously active input messages.
In case you define an input in two different HotKeys, the system will detect the more important Hotkey. The importance is defined by the number of set inputs.
If a HotKey was detected, the controller will call the OnTriggered event and/or an attached TAction instance.
You are allowed to setup your HotKeys at design- and runtime.
LockTime
Set the LockTime property (in milliseconds) to suppress a hotkey for a specific time. Because input commands come very often, f.e. if a button on the gamepad was pressed. As long as you hold the button the system will recognize the hotkey for this button. For sequence detection this may be a problem. Therefor we want to lock the gamepad hotkey for a certain time.
DesignTime
Because HotKeys were implemented by the TCollection component of Delphi, you can setup your combinations at design time.
- Drag a TGorillaInputController component onto your form and doubleclick the “HotKeys” property.
- In the Delphi Collection-Editor you can now add a new TGorillaHotKeyItem.
- Type in a unique name to identify your hotkey, create a OnTriggered event or attach a TAction component to it.
- In the next step we should add the input combination by double clicking the “Combinations” property.
- Here we can now add a new TGorillaHotKeyInputItem.
- In the item select the “Kind” of device.
- and add the message input code (tables below)
- Repeat it for any further inputs you wish to combine.
Runtime
uses Gorilla.Controller.Input, Gorilla.Controller.Input.Consts; procedure TForm1.DoOnHotKey(const AItem : TGorillaHotKeyItem; const ACurrentInput : TGorillaHotKeyRaw); begin DebugOutput(Format('>>> HotKey: %s <<< === %s', [AItem.DisplayName, ACurrentInput.ToString()])); end; procedure TForm1.FormCreate(Sender: TObject); var FInput : TGorillaInputController; LHotKey : TGorillaHotKeyItem; [...] FInput := TGorillaInputController.Create(Self); LHotKey := FInput.AddHotKey('TEST1'); LHotKey.OnTriggered := DoOnHotKey; LHotKey.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_ALT)); LHotKey.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_C)); LHotKey.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_RETURN)); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin FInput.DisposeOf(); FInput := nil; end;
Persistent Messages
Besides the HotKey settings the component supports persistent/continuous messages. A persistent message is a continuously notified message from the system and not a temporary message like a click or key-press.
Persistent messages can not be notified by HotKeys. You need to request those separately.
Supported persistent messages are:
Message-Code | Description | DataType | |
---|---|---|---|
GORILLA_INPUT_MOUSE_POSITION | $100001 | latest mouse position | TPoint |
GORILLA_INPUT_GAMEPAD_TRIGGER_LEFT | $200020 | gamepad left trigger value | TPoint |
GORILLA_INPUT_GAMEPAD_TRIGGER_RIGHT | $200021 | gamepad right trigger value | TPoint |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_LEFT | $200030 | gamepad left thumbstick position | TPointF |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_RIGHT | $200031 | gamepad right thumbstick position | TPointF |
You can request those messages at runtime, like in the following code:
var LMPos : TPoint; LMsg : TGorillaInputMessage; begin // request latest mouse position if FInput.GetPersistentMessage(TGorillaInputDeviceType.Mouse, GORILLA_INPUT_MOUSE_POSITION, LMsg) then begin LMPos := LMsg.Data.AsType<TPoint>(); end else LMPos := TPoint.Zero; // show mouse position in form caption Self.Caption := Format('Gorilla 3D - Mouse: [%d;%d]', [LMPos.X, LMPos.Y]);
Input-Codes for HotKeys
All available InputCodes (TGorillaInputCode) for HotKeys are defined in the Gorilla.Controller.Input.Consts unit.
Keyboard
Constant | Char | Decimal |
---|---|---|
GORILLA_INPUT_KEY_A | 'A' | 65 |
GORILLA_INPUT_KEY_B | 'B' | 66 |
GORILLA_INPUT_KEY_C | 'C' | 67 |
GORILLA_INPUT_KEY_D | 'D' | 68 |
GORILLA_INPUT_KEY_E | 'E' | 69 |
GORILLA_INPUT_KEY_F | 'F' | 70 |
GORILLA_INPUT_KEY_G | 'G' | 71 |
GORILLA_INPUT_KEY_H | 'H' | 72 |
GORILLA_INPUT_KEY_I | 'I' | 73 |
GORILLA_INPUT_KEY_J | 'J' | 74 |
GORILLA_INPUT_KEY_K | 'K' | 75 |
GORILLA_INPUT_KEY_L | 'L' | 76 |
GORILLA_INPUT_KEY_M | 'M' | 77 |
GORILLA_INPUT_KEY_N | 'N' | 78 |
GORILLA_INPUT_KEY_O | 'O' | 79 |
GORILLA_INPUT_KEY_P | 'P' | 80 |
GORILLA_INPUT_KEY_Q | 'Q' | 81 |
GORILLA_INPUT_KEY_R | 'R' | 82 |
GORILLA_INPUT_KEY_S | 'S' | 83 |
GORILLA_INPUT_KEY_T | 'T' | 84 |
GORILLA_INPUT_KEY_U | 'U' | 85 |
GORILLA_INPUT_KEY_V | 'V' | 86 |
GORILLA_INPUT_KEY_W | 'W' | 87 |
GORILLA_INPUT_KEY_X | 'X' | 88 |
GORILLA_INPUT_KEY_Y | 'Y' | 89 |
GORILLA_INPUT_KEY_Z | 'Z' | 90 |
GORILLA_INPUT_KEY_0 | '0' | 48 |
GORILLA_INPUT_KEY_1 | '1' | 49 |
GORILLA_INPUT_KEY_2 | '2' | 50 |
GORILLA_INPUT_KEY_3 | '3' | 51 |
GORILLA_INPUT_KEY_4 | '4' | 52 |
GORILLA_INPUT_KEY_5 | '5' | 53 |
GORILLA_INPUT_KEY_6 | '6' | 54 |
GORILLA_INPUT_KEY_7 | '7' | 55 |
GORILLA_INPUT_KEY_8 | '8' | 56 |
GORILLA_INPUT_KEY_9 | '9' | 57 |
GORILLA_INPUT_KEY_ARROW_LEFT | #37 | 75 |
GORILLA_INPUT_KEY_ARROW_UP | #38 | 72 |
GORILLA_INPUT_KEY_ARROW_RIGHT | #39 | 77 |
GORILLA_INPUT_KEY_ARROW_DOWN | #40 | 80 |
GORILLA_INPUT_KEY_SPACE | ' ' | 32 |
GORILLA_INPUT_KEY_EXCL | '!' | 33 |
GORILLA_INPUT_KEY_QUOTE | '“' | 34 |
GORILLA_INPUT_KEY_HASH | '#' | 35 |
GORILLA_INPUT_KEY_PERCENT | '%' | 37 |
GORILLA_INPUT_KEY_STAR | '*' | 42 |
GORILLA_INPUT_KEY_PLUS | '+' | 43 |
GORILLA_INPUT_KEY_COMMA | ',' | 44 |
GORILLA_INPUT_KEY_MINUS | '-' | 45 |
GORILLA_INPUT_KEY_DOT | '.' | 46 |
GORILLA_INPUT_KEY_DIV | '/' | 47 |
GORILLA_INPUT_KEY_PARA | '§' | 167 |
GORILLA_INPUT_KEY_DOLLAR | '$' | 36 |
GORILLA_INPUT_KEY_EURO | '€' | 128 |
GORILLA_INPUT_KEY_AT | '@' | 64 |
GORILLA_INPUT_KEY_AND | '&' | 38 |
GORILLA_INPUT_KEY_BACKSLASH | '\' | 92 |
GORILLA_INPUT_KEY_BRACKETOPEN | '(' | 40 |
GORILLA_INPUT_KEY_BRACKETCLOSE | ')' | 41 |
GORILLA_INPUT_KEY_EQUAL | '=' | 61 |
GORILLA_INPUT_KEY_QUESTION | '?' | 63 |
GORILLA_INPUT_KEY_TILDE | '~' | 126 |
GORILLA_INPUT_KEY_UNDERSCORE | '_' | 95 |
GORILLA_INPUT_KEY_COLON | ':' | 58 |
GORILLA_INPUT_KEY_SEMICOLON | ';' | 59 |
GORILLA_INPUT_KEY_BAR | | | 124 |
GORILLA_INPUT_KEY_TAGOPEN | '<' | 60 |
GORILLA_INPUT_KEY_TAGCLOSE | '>' | 62 |
GORILLA_INPUT_KEY_SHIFT_BACKSPACE | #8 | 8 |
GORILLA_INPUT_KEY_SHIFT_TAB | #9 | 9 |
GORILLA_INPUT_KEY_LINEFEED | #$0A | 10 |
GORILLA_INPUT_KEY_RETURN | #$0D | 13 |
GORILLA_INPUT_KEY_ESCAPE | #$1B | 27 |
GORILLA_INPUT_KEY_DELETE | #$7F | 127 |
GORILLA_INPUT_KEY_SHIFT_CAPS | #20 | 20 |
GORILLA_INPUT_KEY_SHIFT_LEFT | #160 | 160 |
GORILLA_INPUT_KEY_SHIFT_RIGHT | #161 | 161 |
GORILLA_INPUT_KEY_CTRL_LEFT | #162 | 162 |
GORILLA_INPUT_KEY_CTRL_RIGHT | #163 | 163 |
GORILLA_INPUT_KEY_ALT | #164 | 164 |
GORILLA_INPUT_KEY_ALTGR | #165 | 165 |
Mouse
Constant | Index | Value |
---|---|---|
GORILLA_INPUT_MOUSE_POSITION | 0 | $100001 |
GORILLA_INPUT_MOUSE_LBUTTON | 1 | $100002 |
GORILLA_INPUT_MOUSE_LBUTTON_DBLCLK | 2 | $100003 |
GORILLA_INPUT_MOUSE_RBUTTON | 3 | $100004 |
GORILLA_INPUT_MOUSE_RBUTTON_DBLCLK | 4 | $100005 |
GORILLA_INPUT_MOUSE_MBUTTON | 5 | $100006 |
GORILLA_INPUT_MOUSE_MBUTTON_DBLCLK | 6 | $100007 |
GORILLA_INPUT_MOUSE_X1BUTTON | 7 | $100008 |
GORILLA_INPUT_MOUSE_X1BUTTON_DBLCLK | 8 | $100009 |
GORILLA_INPUT_MOUSE_X2BUTTON | 9 | $10000A |
GORILLA_INPUT_MOUSE_X2BUTTON_DBLCLK | 10 | $10000B |
GORILLA_INPUT_MOUSE_WHEEL | 11 | $10000C |
GamePad
Constant | Index | Value |
---|---|---|
GORILLA_INPUT_GAMEPAD_CONNECTED | $1000 | $200001 |
GORILLA_INPUT_GAMEPAD_DISCONNECTED | $1001 | $200002 |
GORILLA_INPUT_GAMEPAD_DPAD_UP | 0 | $200003 |
GORILLA_INPUT_GAMEPAD_DPAD_DOWN | 1 | $200004 |
GORILLA_INPUT_GAMEPAD_DPAD_LEFT | 2 | $200005 |
GORILLA_INPUT_GAMEPAD_DPAD_RIGHT | 3 | $200006 |
GORILLA_INPUT_GAMEPAD_START_BUTTON | 4 | $200007 |
GORILLA_INPUT_GAMEPAD_MODEBUTTON | 5 | $200008 |
GORILLA_INPUT_GAMEPAD_BACKBUTTON | 6 | $200009 |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_LEFT | 7 | $20000A |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_RIGHT | 8 | $20000B |
GORILLA_INPUT_GAMEPAD_SHOULDER_LEFT | 9 | $20000C |
GORILLA_INPUT_GAMEPAD_SHOULDER_RIGHT | 10 | $20000D |
GORILLA_INPUT_GAMEPAD_BUTTON_A | 11 | $20000E |
GORILLA_INPUT_GAMEPAD_BUTTON_B | 12 | $20000F |
GORILLA_INPUT_GAMEPAD_BUTTON_X | 13 | $200011 |
GORILLA_INPUT_GAMEPAD_BUTTON_Y | 14 | $200012 |
GORILLA_INPUT_GAMEPAD_TRIGGER_LEFT | $100 | $200020 |
GORILLA_INPUT_GAMEPAD_TRIGGER_RIGHT | $101 | $200021 |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_LEFT | $200 | $200030 |
GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_RIGHT | $201 | $200031 |
Sequences
HotKeys are a nice feature, but only work for simultaneous inputs.
Imagine you build a combat game, where attacks or defences are represented by input-combos. For example a user needs to press Button A, then Button B and then Button X to do roundhouse kick. Therefor sequences come in place. Sequences are a descriptive plan of hotkeys, where the order is important.
So Sequence #1
Button A + Button B + Button X
is not the same like
Button B + Button A + Button X
Notes: You are allowed declare an input multiple times in a sequence.
Each sequence can hold up to 16 HotKeys.
DesignTime
Runtime
procedure TForm1.DoOnSequence(const ASequence : TGorillaInputSequenceItem); begin DebugOutput(Format('>>> Sequence: %s <<<', [ASequence.DisplayName])); end; [...] var LSequence: TGorillaInputSequenceItem; LSeq1HK1, LSeq1HK2, LSeq1HK3 : TGorillaHotKeyItem; [...] // create hotkey #1 LSeq1HK1 := FInput.AddHotKey('STRIDE LEFT'); LSeq1HK1.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_A)); // create hotkey #2 LSeq1HK2 := FInput.AddHotKey('FORWARD'); LSeq1HK2.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_W)); // create hotkey #3 LSeq1HK3 := FInput.AddHotKey('STRIDE RIGHT'); LSeq1HK3.AddInput(TGorillaInputDeviceType.Keyboard, Ord(GORILLA_INPUT_KEY_D)); // create the sequence and add the relevant hotkeys LSequence := FInput.AddSequence('TEST SEQUENCE', [LSeq1HK1, LSeq1HK2, LSeq1HK3]); LSequence.OnTriggered := DoOnSequence;
Feedback Events
Now that we've covered hotkeys and sequences, another way of detecting inputs are the raw feedback events of the input controller itself.
The input controller provides a few feedback events that work parallel to the mentioned hotkey or sequence detection.
Event | Type | Description |
---|---|---|
OnMouseDown | TGorillaInputMouseEvent | Raw mouse down event callback thrown by the mouse handler. This event will be synchronized with main thread context. |
OnMouseUp | TGorillaInputMouseEvent | Raw mouse up event callback thrown by the mouse handler. This event will be synchronized with main thread context. |
OnMouseDblClick | TGorillaInputMouseEvent | Raw mouse double click event callback thrown by the mouse handler. This event will be synchronized with main thread context. |
OnMouseMove | TGorillaInputMouseMoveEvent | Raw mouse move event callback thrown by the mouse handler. This event will be synchronized with main thread context. |
OnMouseWheel | TGorillaInputMouseWheelEvent | Raw mouse wheel event callback thrown by the mouse handler. This event will be synchronized with main thread context. |
OnKeyDown | TGorillaInputKeyEvent | Raw key down event callback thrown by the keyboard handler. This event will be synchronized with main thread context. |
OnKeyUp | TGorillaInputKeyEvent | Raw key up event callback thrown by the keyboard handler. This event will be synchronized with main thread context. |
OnGamePadConnect | TGorillaInputGamePadEvent | Raw callback when a new gamepad controller was connected. This event will be synchronized with main thread context. |
OnGamePadDisconnect | TGorillaInputGamePadEvent | Raw callback when a gamepad controller was disconnected. This event will be synchronized with main thread context. |
OnGamePadButtonDown | TGorillaInputGamePadButtonEvent | Raw gamepad button down callback thrown by gamepad handler. This event will be synchronized with main thread context. |
OnGamePadButtonUp | TGorillaInputGamePadButtonEvent | Raw gamepad button up callback thrown by gamepad handler. This event will be synchronized with main thread context. |
OnGamePadTrigger | TGorillaInputGamePadTriggerEvent | Raw gamepad trigger callback thrown by gamepad handler. This event will be synchronized with main thread context. AState = (GORILLA_INPUT_GAMEPAD_TRIGGER_LEFT, GORILLA_INPUT_GAMEPAD_TRIGGER_RIGHT) |
OnGamePadFeedback | TGorillaInputGamePadFeedbackEvent | Raw gamepad feeback callback for receiving thumbstick position, thrown by gamepad handler. This event will be synchronized with main thread context. AState = (GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_LEFT, GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_RIGHT) |
Feedback Event Methods
Declaring a feedback event method needs further unit integration in most cases for the used types and constants:
- Gorilla.Controller
- Gorilla.Controller.Input.Consts
- Gorilla.Controller.Input.Types
Type | Declaration | Notice |
---|---|---|
TGorillaInputMouseEvent | procedure(ASender : TObject; AStates : TGorillaMouseStates; APos : TPointF) of object; | For available mouse states, read more here |
GorillaInputMouseMoveEvent | procedure(ASender : TObject; AStates : TGorillaMouseStates; APos : TPointF) of object; | For available mouse states, read more here |
TGorillaInputMouseWheelEvent | procedure(ASender : TObject; APos : TPointF; const AWheelDelta : Integer) of object; | |
TGorillaInputKeyEvent | procedure(ASender : TObject; AKeyCode : Integer) of object; | |
TGorillaInputGamePadEvent | procedure(ASender : TObject; AState : TGorillaInputCode; AUser : Integer) of object; | |
TGorillaInputGamePadButtonEvent | procedure(ASender : TObject; AState : TGorillaInputCode; AButtons : TGorillaGamePadButtons) of object; | For available gamepad buttons, read more here |
TGorillaInputGamePadTriggerEvent | procedure(ASender : TObject; AState : TGorillaInputCode; const AButtons : TGorillaGamePadButtons; AValue : Byte; AFlags : UInt64) of object; | AState = (GORILLA_INPUT_GAMEPAD_TRIGGER_LEFT, GORILLA_INPUT_GAMEPAD_TRIGGER_RIGHT) |
TGorillaInputGamePadFeedbackEvent | procedure(ASender : TObject; AState : TGorillaInputCode; const AButtons : TGorillaGamePadButtons; APos : TPointF; AFlags : UInt64) of object; | AState = (GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_LEFT, GORILLA_INPUT_GAMEPAD_THUMBSTICK_POS_RIGHT) |
Types
TGorillaInputMode = (Activated, Deactivated); TGorillaMouseState = (LButtonDown, LButtonUp, RButtonDown, RButtonUp, MButtonDown, MButtonUp, X1ButtonDown, X1ButtonUp, X2ButtonDown, X2ButtonUp, LButtonDblClick, RButtonDblClick, MButtonDblClick, X1ButtonDblClick, X2ButtonDblClick, MouseMove, MouseWheel); TGorillaMouseStates = set of TGorillaMouseState; TGorillaGamePadButton = ( None, // directional pad up DPadUp, // directional pad down DPadDown, // directional pad left DPadLeft, // directional pad right DPadRight, // start button StartButton, // mode button ModeButton, // back button BackButton, // left thumbstick pressed LeftThumbStick, // right thumbstick pressed RightThumbStick, // left shoulder button LeftShoulder, // right shoulder button RightShoulder, // button A ButtonA, // button B ButtonB, // button X ButtonX, // button Y ButtonY ); TGorillaGamePadButtons = set of TGorillaGamePadButton;
Debugging-Issue
On Windows platform we're using low level hooks for keyboard and mouse detection, to support high-performance and accuracy.
But this comes with a major issue: In many cases debugging is getting very slow, because hooks have a 1000ms timeout by default and hooks not getting paused during debugging.
This leads to lagging mouse and keyboard input.
Currently this can not be solved out of the box!
But you can add a registry entry to your system, like this:
- Open RegEdit.exe (Windows)
- Move to the path: “HKEY_CURRENT_USER\Control Panel\Desktop\”
- Add a new value of type DWORD with the name “LowLevelHooksTimeout”
- Enter a value which stands for the timeout in milliseconds (f.e. 50ms)
- A system restart is required!
Next: Pathfinding