TLbind
Contents
About
TLbind is a system which turns a mass of keyboard/joystick input into easy-to-use game-oriented controls. Making this divide also makes it easy to rebind controls, enable or disable keyboard, mouse, or joystick input at will, and make digital and analogue input work easily together. It's goal is to make it simple to add professional-style configurable "2 sets of keys + optional joystick" controls to Love2D games.
It's under the ZLIB license.
Download
Contact
- Forum thread
- Taehl - SelfMadeSpirit@gmail.com
Setup
Procedural style
- Add TLbind.lua to your game's folder
- In love.load, add the code
TLbind,control = love.filesystem.load("TLbind.lua")()
- In love.update(), add the code
TLbind:update()
Object-oriented style
- Add TLbind.lua to your game's folder
- Assuming your player object is called "player", and you want its controls to be listed in player.control, use the code
player.binds, player.control = love.filesystem.load("TLbind.lua")()
- In your player object's update function (or otherwise once per frame), call
player.binds:update()
- TLbind itself acts like an object, so you can give each player their own separate TLbind (using
TLbind.giveInstance()
), if you so desire (could be handy for splitscreen)
FAQ
- Q) How do I know when a control is pressed?
- A) Whenever a control is pressed or held, control[control name] will be true. You could use it with a statement like
if control.up then
...
- Q) How do I change a control?
- A) To bind a "fire" control to the third button of joystick 1, for example, you could use
TLbind.joyBtns[1][3]="fire"
. From then on, control.fire would be true whenever Joy1 button 3 is pressed.
- Q) How do I know if the player has simply pressed the button, instead of holding it? What if I want to know when it's released?
- A) When any control is first pressed, control.tap[control name] will be true for one frame. Likewise, when it's released, control.released[control name] will be true for one frame. These would be used the same way you'd use a normal control -
if control.tap.jump then
. You can also use the control.controlPressed and control.controlReleased callbacks (see below).
- Q) How does TLbind handle mouse input?
- A) Clicking is bound to digital controls, like a joystick button. Cursor position is an analogue pair where the center of the screen is (0,0), the top-left corner is (-1,-1), and the bottom-right corner is (1,1). If the mouse position is bound to the same control as joystick axes, then the control will only use the mouse position when the joystick axes are centered. If you can't seem to move the mouse axes, ensure your joystick is centered (both axes are 0).
- Q) What functionality does TLbind have?
- A) It separates controls (things your game cares about "jump", "left", etc.) and input (spacebar, joystick coordinates, etc.). This allows a game's controls to be easily configured at any time; lets a game have multiple inputs per control (or controls per input); and makes coding simpler. It makes it easy to bridge analogue and digital control; disable keyboard or joystick input easily; detection of taps instead of holding (no more "hold jump to bounce over and over the second you touch the ground"); and can apply analogue "deadzones" (fixes the "drifting when not touching the stick" problem) and normalizing (preventing the all-too-common "you run faster diagonally" bug).
- Q) Is it hard to use?
- A) I tried to make it as simple as possible, and list example binds below. With six lines of code, you can give your game industry-standard WASD + Arrows + Gamepad controls.
- Q) What's the difference between digital and analogue controls, and how do you make them work together?
- A) In TLbind, digital controls are keys or buttons, and are represented as either true or false. Analogue controls take input from joystick axes or balls, and are represented as a decimal between -1 and 1, with 0 being centered. TLbind lets you map an analogue control to two digital controls, which makes the analogue input trigger the digital controls and the digital input push the analogue control to -1 or 1. As an example: If using the example binds below, pressing the joystick a little to the left might make control.horiz = -0.22191, control.left = true, and control.right = false.
Functions
bind:update
TLbind:update()
This updates the controls, and thus should be called once each frame. Please note that it has a colon, not a period.
TLbind.giveInstance
TLbind.giveInstance( binds )
This will create a new set of controls from the given binds table. Returns the new instance's binds, control, control.tap, and control.release. Good for OO-style programming, multiplayer controls, etc..
table binds
- A table of binds, as explained below.
control.controlPressed
control.controlPressed(c)
This callback works just like love.keypressed, except for controls (meaning it'll work properly with binding changes and joystick input). It gets called exactly once when a control is activated.
string c
- The name of the control that was pressed.
control.controlReleased
control.controlReleased(c)
This callback works just like love.keyreleased, except for controls.
string c
- The name of the control that was released.
Input types and configuration
Bindings are managed under TLbind (procedural example / default) or player.binds (OOP example). A list of bindings is a table, grouped together by input, joystick number, etc.. For clarity, please refer to the following terminology:
- Control - an abstract, game-centric variable which will either equal true/false (for digital controls) or a number between -1 and 1 (analogue controls). Controls can have any name you want to give them, but each name must be unique.
- Input - the signal a keyboard or joystick gives off when manipulated. Love2D defines these as KeyConstants, MouseConstants, JoystickConstants, joystick numbers, and joystick button, axis, hat, and ball numbers.
- Bind - the link between input and controls. "When this input is triggered, set that control". Each input can be bound to only one control, but a control may be bound to many inputs.
- Map - links an analogue control to two digital controls. Mapping makes it simpler to work with analogue controls since you don't have to worry about whether or not the player is using analogue or digital input.
bind.keys
This is for keyboard keys. It's a table where the keys are KeyConstants and the values are the names of controls. In other words, you make bindings in the format TLbind.keys.KeyConstant = "control"
. See
TLbind.keys = {
w="up", a="left", s="down", d="right", [" "]="jump", lctrl="attack", escape="menu",
up="up", left="left", down="down", right="right", z="jump", rctrl="attack",
}
bind.mouseAxes
This is a table that binds the mouse x and y coordinates. It's in the form of TLbind.mouseAxes[1 for x or 2 for y] = "control"
.
TLbind.mouseAxes = { "horiz", "vert" }
bind.mouseBtns
This lists mouse buttons, in the form of TLbind.mouseBtns.MouseConstant = "control"
.
TLbind.mouseBtns = { l="target", r="shoot" }
bind.joyAxes
This is the list of joystick axes (the stick itself or gamepad thumbstick(s), but may also include rudders, etc.). Using bind.deadzoneAxes on axis pairs is highly recommended. It's in the form of TLbind.joyAxes[joystick#][axis#] = "control"
.
TLbind.joyAxes = { {"horiz", "vert"} }
bind.joyBtns
This lists joystick buttons, in the form of TLbind.joyBtns[joystick#][button#] = "control"
TLbind.joyBtns = { {"jump", "attack", [8]="menu"} }
bind.joyBalls
This is for any ball device a joystick may be sporting. Since these are rare, it's advisable to not make your game depend on using one. Their binds are in form of TLbind.joyBalls[joystick#][ball#] = {"x control", "y control"}
TLbind.joyBalls = { { {"horiz","vert"}, {"yaw","pitch"} } }
bind.joyHats
This table is for joystick hats (those mini-thumbstick things seen on some joysticks). Please note that they're digital, not analogue. They're bound in the form of TLbind.joyHats[joystick#][hat#] = {"l control", "r control", "u control", "d control"}
TLbind.joyHats = { {{"left","right","up","down"}} }
bind.maps
This table lists the analogue controls that should be mapped to and from digital controls. Using this, you can easily code for one type of input without having to worry about coding for the other type. They're created in the form of TLbind.maps[analogue] = {"negative digital", "positive digital"}
TLbind.maps = { horiz={"left","right"}, vert={"up","down"} }
bind.deadzoneAxes
This feature applies scaled radial deadzones to a pair of analogue controls. This prevents "stick drift" on old/cheap/uncalibrated devices. It also restricts the analogue pair to the inside of a circle, rather than the default square. Using this, you can avoid the classic "you move faster diagonally" bug. Pairs will reach about 0.7 when at a perfect 45-degree angle (but still reach a full 1 when straight). It's made in the form of TLbind.deadzoneAxes[entry#] = {"analogue 1", "analogue 2"}
TLbind.deadzoneAxes = { {"horiz", "vert"} }
A note about Xbox controllers
Microsoft has decided that Xbox controllers are the official gamepad for Windows, and as such, are the most common controller a player may have. Therefore, it's advisable to tailor your game's controls to work with them.
One important caveat about Xbox controllers: Both of their triggers (L2 and R2 for those of you more familiar with Playstations) are treated as one axis, rather than two buttons! Consider using the following binds to make up for it:
joyAxes = { {[3]="triggers"} } -- r trigger is -1!
maps = { triggers={"rtrig","ltrig"} } -- ltrig and rtrig are the digital controls