In Trial getting inputs from the user is divided up into three parts: raw input events, actions, and mappings.
Raw device inputs are delivered via the event system. Each class of device has its own events derived from
Trial's systems allow distinguishing between multiple gamepads, but not between multiple keyboards or mice.
You should almost never have to directly interface with these events, as they are not user-configurable. Instead, you should use the input mapping system.
Input events are provided by the rendering backend and cl-gamepad, and should automatically appear in the
scene of your
actions are abstract events that correspond to in-game actions. Actions are typically mapped from existing events, and you can look at the origin with
source-event. New actions are defined with
define-action. The superclass list is used to attach the event to one or more
action-sets are ways to group actions together under a set that allows you to activate and deactivate all actions at once. This way you can have a separate sets of actions for menu navigation, for in-game, etc. Sets can be activated by setting
active-p. An action will be active as long as at least one of the sets it is a subclass of is active.
Often it's also desired to have
action-sets be mutually exclusive. In that case, making the sets a subclass of
exclusive-action-set will ensure all others will be automatically deactivated when another is activated.
Most actions will represent some kind of event request, such as "jump", "interact", "select next", "confirm", etc. For some games however it can also make more sense to have actions that carry a value, such as "gas" or "camera tilt". For the latter, the superclasses
Actions by themselves allow you to define player input with a layer of abstraction, but they won't be useful unless actions can be fired by user inputs. This is where the mapping layer comes into play.
Event mappings are executed through
map-event, which is handled by the
controller when it is present in the scene. If you do not have a controller present, you should call
map-event yourself somehow.
Defining how events are mapped is, in the most generic way, done via
define-mapping-function. The function it defines is invoked for each event, and can then just issue other events back onto the loop. Typically though this is too generic and open-ended, and also doesn't allow players to customise how the mapping works.
keymap.lisp file should be defined, which describes the mappings. This file will contain all the default mappings present, and Trial will emit a new file of the same structure when the user changes the mappings. Trial will also take care of keeping track of the action's state. For digital actions this means whether it's "active" or not, and can be retrieved with
retained. For directional actions this means the current direction vector, which can be retrieved with
The file describing action mappings is in s-expression format and contains definitions like this:
(trigger jump (key :one-of (:space)) (button :one-of (:b :a))) (directional movement (stick :one-of ((:l-h :l-v) (:r-h :r-v) (:dpad-h :dpad-v))) (buttons :one-of ((:dpad-u :dpad-r :dpad-d :dupad-l))) (keys :one-of ((:w :a :s :d))))
Meaning: when the
:space key is pressed, or when the
:a button on a gamepad is pressed, fire a
jump action. Map the left and right analog sticks, the dpad, and the wasd keys to the movement directional action.
The possible mapping types are
trigger for digital maps, and
directional for directional maps.
Querying and interacting with the mappings can of course also be done programmatically outside of the
keymap.lisp source file. On the most basic, one can
save-mapping to interact with the file. Finding mappings that spawn an action can be done via
find-action-mappings. It can also be useful to capture an input event and turn that into on
action-mapping or vice-versa. To do so, simply use
event-to-action-mapping, or the inverse,
The possible binding sources are
key for keyboard keys,
button for gamepad buttons,
axis for gamepad axes, and
mouse for mouse buttons. Each of the bindings also accepts the following parameters:
:one-of the qualifiers to look out for, typically the names of the buttons
:threshold the value at which the input becomes "active". Defaults to 0.5, but only used for
:toggle if NIL (default) then the action is retained while the input is "active". Otherwise the action stays retained until the input becomes "inactive" and then "active" again. Only used for
:value what value should be used for the action when the input is "active". Only used for
bind mappings on non-
The possible binding sources are the same as for triggers, but with the additional
point for relative mouse movement,
stick to combine two analog axes,
buttons to combine four gamepad buttons, and
keys to combine four keyboard keys. The following parameters can be set depending on the binding source:
:axis The axis that the event maps to. Can be
:one-of Same as for triggers, except for
keys, where each qualifier should be a list. For sticks,
(X Y), for buttons and keys
(Up Right Down Left)
:dead-zone The dead-zone for
axis below which no direction is recorded. Defaults to 0.1
:low-value For digital events, the value in the "low"/"up" state. Defaults to 0.0
:high-value For digital events, the value in the "low"/"up" state. Defaults to 1.0
point the timeout after which the mouse is reset to no movement. Defaults to 0.02
point, the scaling applied to the mouse direction. Allows both mouse speed correction as well as flipping of the mouse axes. Should be
(X Y) scalar list, and defaults to
Sometimes, particularly when switching action sets, it can be useful to reset or clear retentions.
clear-retained will reset all retention information.
reset-retained will attempt to "back fill" retention information based on current device state.
It can also be useful to inhibit mapping of any key events, such as when the user is typing into a text field, as then key presses could instead lead to unintended actions being taken. To control the mapping simply set
Finally, default retentions for all mouse buttons and keyboard keys are kept. The retention is simply named after the key or button. These retentions can be useful for debugging purposes.