Today’s post we’ll go over the implementation of the InputManager class!

Bindings and Bindings Config

The bindings have a pretty simple JSON schema. The root object is an array. Each binding in the array takes this format:

{
	// This is the name of the alias the binding corresponds to.
	"Alias": "name_of_alias",

	// Binding is either a single input code, or an array of input codes.
	// If Binding is an array, all of the input codes must be pressed or active to trigger the binding.
	// For example, if "Binding": ["Left Shift", "A"] is given,
	// then the Left Shift and A keys must be pressed before the value of "Alias" is updated.
	// If one of the keys in the binding is released, then the whole alias value will be reverted back to zero.
	"Binding": "name_of_keycode_mousecode_or_joystickcode",
	// or
	"Binding": ["input_code_a", "input_code_b", "etc."],

	// Scales the input values by this number.
	// Optional. If not provided, will default to 1.0.
	// For example, a button input value of [0, 1] will become [0, scale].
	// For axis inputs like a mouse or joystick axis, the range will become [-axis_max * scale, axis_max * scale].
	"Scale": 1.0,

	// The number of taps from the binding to trigger a value update.
	// Optional. If provided, value must be at least 1.
	// This value is used to cause the alias to update when this many taps occurs.
	// For example, a value of 2 means that the alias will update when the "Binding" is double tapped.
	// When the taps option is provided, the alias value will only be updated on the frame the number of taps occurs. It will be cleared on the next frame.
	"Taps": 2,

	// The amount of time in seconds between keypressed to consider another input a tap.
	// Optional.
	// For example, if "Taps": 2 and "Bindings": "A", if we press the A key once, we have 0.1 seconds to release the A key and press it again to increase the tap count to 2.
	"Tap Interval": 0.1,

	// The Input Modes these bindings are active in. If the InputManager is not in this mode, the binding will always return a value of 0.
	// Optional. If not given, defaults to "Default".
	"Modes": "mode_name",
	// or
	"Modes": ["mode_name_a", "mode_name_b"]
}

Here’s a simple example using scale to bind WASD to movement and mouse look.

[
	{ "Alias": "Horizontal", "Binding": "A", "Scale": -1.0 },
	{ "Alias": "Horizontal", "Binding": "D", "Scale": 1.0 },
	{ "Alias": "Vertical", "Binding": "S", "Scale": -1.0 },
	{ "Alias": "Vertical", "Binding": "W", "Scale": 1.0 },
	{ "Alias": "Camera_Pitch", "Binding": "Mouse Delta Y", "Scale": 1.0 },
	{ "Alias": "Camera_Yaw", "Binding": "Mouse Delta X", "Scale": 1.0 },
]

Input Mode

The InputManager has a simple “state machine”. It’s simple in the sense that it doesn’t have a data structure or anything that maintains edges and conditions. You just ask it to be in a state with a given name, and it will be in that state. The only data it maintains is the current state and the previous state. This enables you to toggle into a state, and retain where you were and toggle back when done.

The main use case for input modes is to selectively enable and disable certain bindings. An example of this is when you press F9 for the Debug Menu, it changes the input mode so the camera won’t move while trying to use the mouse to navigate the menus.

See You Next Time Space Cowboy

The InputManager is not a complicated manager. It provides basic functionality for creating input bindings and sampling and updating their values. Anything more than that should live in a higher layer.

See you next time!