Common Organization of Controls Kit Manual

Basic use

The library designed to be reasonably easy to use while maintain great flexibility and features.

Preparations

First thing you need to do is, obviously, require the library. Then, you create a new control object using cock.new. You can optionally fill it with data at instant, or may do it later. Then you optionally load saved settings, via serialization for an instance. After that (but not before!) you can reload joystick with cock.reloadJoysticks, that essentially will search for previously used joystick and set it up. If you don't have controls to load, you assign default controls, joystick modes and mouse values with cock.setControls (if you didn't do it upon creation).

Controls data format

Functions cock.new and cock.setControls are accepting data in specific format that needs to be maintained. It is defined as following:

{ default1 = {
  up = { primary = { "keyboard", "up" }, secondary = { "mouse axis", "y", "-", "++" },
  down = { primary = { "keyboard", "down" }, secondary = { "mouse axis", "y", "+", "++" } },
 default2 = { ... }
}

It will define you controls "up" and "down" to be accessed later, with two options to use ("primary" and "secondary"), one with keyboard only control and another with mouse only, and pushes it to "default1" controls set. You can then use defined defaults with cock.setDefault to e.g. restore controls to default, or set them up as such if there's no user-edited controls.

Obtaining the values

This is done really easy: you simply access your control class "current" or "previous" table's child named as your defined control.

if control.current.up > 0 then 
    player.y = player.y - 100 * dt 
elseif control.current.down > 0 then 
    player.y = player.y + 100 * dt 
end

These variables (except for mouse axis) contain decimal values ranged -1 to 1 (0 to 1 in negative cutoff mode), which you can use for analog control. Mouse axis-bound variables would store pretty arbitrary numbers, with undefined range, only influenced by mouse factor and offset.

You can also use library callbacks just like normal input callbacks:

function cock.controlpressed ( id, key, val )
    if key == "up" then
        player.accel = 1
    end
end

By default, all library callbacks are disabled, so to use them you have to enable them via cock.setCallbacks.

The library also have effecient getters and setters for that, but you are strongly discouraged to use them.

Updating controls

You simply put update function to your "love.update" callback, and that's it. It is suggested to put it to beginning of the function. Placing it to other callbacks, such as "love.keypressed" is not necessary at all due to LÖVE's way of handling them.

Enabling user to edit controls

The library have special function to assume "input capture" mode, in which any incoming user input will be captured and automatically binded to provided key. You may specify whether or not you want to lock input callbacks and update function, and ignore mouse motion, during input capture mode. You can abort this mode using cock.cancelCapture. In input capture mode, if mouse motion capture is enabled, the library will temporairly center the mouse and then set it back once input is captured, but it will not hide it because, with all the possible user implementations of mouse cursor, it's simply meaningless, so make sure you hide your cursor during capture, otherwise it'll weirdly jump to the center of the screen and back. In this case you would normally want to use cock.controlcaptured callback, with cock.bind at the end.

You can bind the device and key manually, using the cock.bind function.

You can implement "arbitrary binds per action" using search for unused options via cock.getEmptyOption. It will return next unused option, and since for gathering input, option name is irrelevant, you can simply bind keys to free options arbitrairly. If the function fails to find empty option, you'd have to create a new option with cock.addOption rather than binding to unexisted option. This is due to internal structure of the library.

NOTE: in input capture mode, the library would temporary replace your input callbacks and update function with it's own wrappers for them, until some input is provided, which could interfere with other libraries/modules that would do the same (or even with itself), so use it with care. Make sure you do not assume another input capture while being in input capture, this creates an infinite recursion and the game will instantly bluescreen with a stack overflow error. The rule of thumb is to only allow one callbacks replacement at a time, or only allow nested replacements, but never intersecting replacements. If none of your other code would do that in a separate thread, but would do that in main thread, enabling both locks ensures safe capture. If your code doesn't do that at all, having locks enabled may be not necessary.

Advanced features

This library was created with robust design in mind, so it supports a bit of handy advanced features that otherwisely would be needed to implemented by the programmer, which is not always convenient.

Mouse offset and scaling factor

For simplicity of aquiring values from control table, it handles this two math operations for you. It subtracts actual mouse position from an offset (cock.setMouseOffset), and then multiplies result by scaling factor(cock.setMouseFactor), per axis. This creates an easy way to set mouse centre point and mouse sensevity.

Delta and inversion modes, cutoff modes

Inversion mode is pretty straightforward, it simply negates input. Delta mode is a little bit trickier. In this mode, instead of reporting absolute value, the control object reports delta value, that is how much input have changed since last update. Both delta and inversion modes have "negative cutoff" mode to prevent it from returning negative values. Delta mode and inversion mode are both having "negate" mode that work together: any negatives negates the output, and double negative makes positive. This may seem to be excessive, but actually it's a lot simplier to do it that way. There's also one special mode that converts -1..1 range to 0..1 range, which is particularry useful for HOTAS controllers and XBox360 triggers.

Delta mode is particularry useful when used with joystick; this opens possibility to implement obscure and cunning ways of using analog sticks and XBox360 triggers, that aren't normally can be seen in the games. When used with mouse, this simply returns distance travelled from previous coordinate. Note that if you want FPS-like controls where you would constantly set mouse back to the center of the screen, you should instead use "bypass" mode, since in this state, delta mode would yield acceleration of the mouse rather than distance travelled. You can still use that nevertheless, if you may.

Joystick hat modes

Default mode for joystick hat is 8-way diagonal, the other general mode is 4-way. But, since that mode would discard all diagonal input, the update handler have 5 modes that specifically tell how to resolve diagonal input:

  • Prefer vertical input
    • convert diagonal up to up and diagonal down to down
  • Prefer horizontal input
    • convert diagonal left to left and diagonal right to right
  • Prefer clockwise input
    • "turn" diagonal input 45 degrees clockwise, e.g. down-left becomes left, etc.
  • Prefer counter-clockwise input
    • "turn" diagonal input 45 degrees counter-clockwise, e.g. down-left becomes down, etc.
  • Convert diagonal to adjacent lateral
    • convert diagonal up and left to both lateral up and lateral left

As the name suggests, first two modes are axially biased and will more likely result in getting corresponding direction when actively working on D-pad, and this is more of a gameplay-related setting than actual adjustment, e.g. if you use left and right to move the block in a tetris game, having it accidntally dropped because player would hit the D-pad diagonally down is not desired, and this is when you enable horizontal bias.

The other two are radially biased (in some way they are unbiased) and although they don't make any direction pressed any more likely, they make changing D-pad direction more fluent in one way and more stiff in the other way. This modes give better feel of purely 4-directional D-pad on immediate hat presses, while maintaining only allowing one direction at a time. You may, in fact, just randomly use either of them, but you may also provide choosing between them as an option, since not all joysticks' hats are made so great, and specific mode enabled may actually improve players' accuracy.

The last mode doesn't actually adjusts anything and just passes by the input, "splitting" it in two.

You can change the modes on fly as necessary with cock.setJoystickHatMode.

Deadzones

You can define deadzones per-axis with cock.setJoystickDeadzone. This function works pretty straightforward. You might want to know a little detail about it's implementaiton, that is it first subtracts threshold from the value and then amplifies it by another number that results in axis being "shrinked", starting at threshold point and ending at 1 like normal, rather than instantly jumping from 0 to threshold parameter and such.

Joystick hat remapping

Due to the way some joysticks' hats are handled, you may want to map some of joysticks' buttons to a virtual joystick hat with cock.remapJoystickHat. To do that, you simply use corresponding function and pass it exactly 4 joystick buttons to use. After that, the joystick will have specified hat "remapped", and it'll work like regular joystick hat.

Displaying setup

Library provides handful of feedback functions, such as conversion from literal to numerical values and vice versa, for returning binded controls, for finding unused options.

Function index

cock.addJoystickHooks up new joystick.
cock.addOptionCreates new input option.
cock.bindBinds given input.
cock.cancelCaptureCancels input capture.
cock.convertAxisConverts axis.
cock.convertDeltaConverts delta.
cock.convertDeviceConverts device.
cock.convertInverseConverts inverse.
cock.convertJoystickConverts joystick.
cock.convertJoystickHatConverts joystick hat.
cock.convertJoystickHatModeConverts hat mode.
cock.convertKeyConverts key.
cock.deleteDeletes object.
cock.deleteJoystickDeletes joystick.
cock.deleteOptionDeletes input option.
cock.explodeCapturedDataExplodes longdata.
cock.findFind object.
cock.getBindedGets binded controls.
cock.getCaptureGets capture state.
cock.getEmptyOptionFinds unused option.
cock.getJoystickDeadzoneGets deadzones.
cock.getJoystickHatModeSets hat modes.
cock.getJoysticksListLists joysticks.
cock.getMouseFactorGets mouse factor.
cock.getMouseOffsetGets mouse offset.
cock.newCreates new object.
cock.reloadJoysticksReloads joysticks.
cock.remapJoystickHatMaps buttons to hats.
cock.setCallbacksToggles callbacks.
cock.setCaptureUser-edit controls.
cock.setControlsCreates control layouts.
cock.setDefaultReverts to defaults.
cock.setDefaultXBox360(BROKEN) Creates XBox360 layout.
cock.setJoystickDeadzoneSets deadzones.
cock.setJoystickHatModeSets hat modes.
cock.setMouseFactorSets mouse factor.
cock.setMouseOffsetSets mouse offset.
cock.unbindUnbinds map.
cock.updateUpdates object.
cock.updateAllUpdates all objects.

See also