KeyMap - A Keymapping Module with Controller Support
Posted: Tue Jan 07, 2014 10:38 pm
Tired of constantly calling love.keyboard.isDown(key)? Want to check for more than one key with a single line of code?
I have just the thing for you!
As a part of my project, I felt like I spent entirely too much time screwing around with detecting keys. When you need more than one input to do one thing (WSAD, or arrow keys, or gamepad), it is a real pain. So, I spent some time and developed "KeyMap." With it, there are only a few functions to get it running, and with those few functions, you can have as many keys as you want bound to as many mappings as you want. I've attached a demo program so you can see how it's done.
DEMO
Check out the binding demo I've made for you! It shows all of the states of each mapping, what keys are bound, and even lets you bind and unbind each mapping. In the attachment is the module as well as a demo program. It has five built-in mappings, and displays all of the states. Click the "BIND" button next to the mapping you wish to bind to. Press escape to cancel, click the button again to unbind all mappings, or press any key, button, or analog stick to bind.
Here is just the module with a tad bit of documentation. It is fairly straightforward.
KeyMap.lua
Setup
First off, copy KeyMap.lua into the root directory of your project.
At the top of your main.lua file, add in the following line of code:
To add in a mapping (a list of keys to look for), use the following line of code in your love.load function:
We just added 5 mappings to KeyMap. Now, there are no keys assigned to it. We need to bind a key to a mapping. There are two ways of doing this.
If you know what KeyConstants you want to assign, we can go ahead and assign them manually like so:
Here, we told KeyMap that we want to bind the "W" key and the up arrow on the keyboard to the "up" mapping. "key" is the KeyType, which will be either "key" for keyboard or "joy" for joystick. These are separated because some KeyConstants are the same as some GamepadButton constants, such as 'a' being both the "A" key on the keyboard and the "A" button on the gamepad.
The other way we can bind to a mapping is to call the pass in what mapping we want to bind to with the following function. It will take whatever key, button, or analog stick that is pressed.
Next, you will need to call the following in your love.update(dt) method (NOTE: call this after your detection codes, or isPressed method will not ever return true):
That is all you need to get set up!
All that is left is to check if our bindings worked. Anywhere you need to check for keypresses, use the following line of code to check whether the button is down:
This function will return true if any of the keys mapped to "up" are being pressed, and obviously false otherwise.
Now, what if we want to see if the button has just been pressed? Normally, we would need to check for that in love.keypressed(key,isrepeat), but now we just do:
Additionally, we can check to see if the button is being held down. It does not return true when the mapped key is just being pressed.
I hope you all find this to be useful! I know I have
EDIT 01/15/14: Updated code to be an actual module rather than whatever it was before.
I have just the thing for you!
As a part of my project, I felt like I spent entirely too much time screwing around with detecting keys. When you need more than one input to do one thing (WSAD, or arrow keys, or gamepad), it is a real pain. So, I spent some time and developed "KeyMap." With it, there are only a few functions to get it running, and with those few functions, you can have as many keys as you want bound to as many mappings as you want. I've attached a demo program so you can see how it's done.
DEMO
Check out the binding demo I've made for you! It shows all of the states of each mapping, what keys are bound, and even lets you bind and unbind each mapping. In the attachment is the module as well as a demo program. It has five built-in mappings, and displays all of the states. Click the "BIND" button next to the mapping you wish to bind to. Press escape to cancel, click the button again to unbind all mappings, or press any key, button, or analog stick to bind.
Here is just the module with a tad bit of documentation. It is fairly straightforward.
KeyMap.lua
Code: Select all
-- @module keymap
local keymap = {}
local _keyCheck = false
local _keyCheckFor = false
local _keyLast = false
local _map = {}
local joystick = love.joystick.getJoysticks()[1]
local lastPushed = nil
love.keyboard.setKeyRepeat( true )
-- Add a new mapping to keymap
function keymap:addMapping( maps )
for _ , m in pairs( maps ) do
_map[m] = { held=false , pushed=false , keys={} , joy={} }
end
end
-- Retreive mapping from id
function keymap:getMapping(id)
if id then
return _map[id]
else
local mapList = {}
for i , m in pairs(_map) do
mapList[i] = m
end
return mapList
end
end
-- Retreive a mapping from key
local function findMap(keyType, key)
for i , map in pairs(_map) do
if keyType == "key" then
for j , k in pairs(map.keys) do
if k == key then
return map
end
end
else
for j , k in pairs(map.joy) do
if k == key then
return map
end
end
end
end
end
-- Check if system is checking for keys to bind
function keymap:isBinding()
return _keyCheck
end
-- Call this in your main code.
function keymap:update( dt )
if _keyCheck then
if _keyLast then
self:bind(_keyCheckFor,_keyLast)
_keyCheck = false
_keyLast = nil
_keyCheckFor = nil
end
else
for i,m in pairs(_map) do
if m.pushed then
m.pushed = false
m.held = true
end
end
end
end
--Check if any key in the given mapping is either being pushed or held.
function keymap:isDown( map )
if map ~= nil then
return _map[map].held or _map[map].pushed
end
end
-- Check if any key in the given mapping has been recently pushed
function keymap:isPushed( map )
if map ~= nil then
if _map[map].pushed then
--_map[map].pushed = false
return true
end
return false
end
end
-- Check if key is being held down
function keymap:isHeld( map )
return _map[map].held
end
-- Bind key pushed ("id") to mapping ("map")
function keymap:bind( map , id )
if id[1] == "joy" then
for i , m in pairs( id ) do
if i > 1 then
table.insert( _map[map].joy , id[i] )
end
end
else
for i , m in pairs( id ) do
if i > 1 then
table.insert( _map[map].keys , id[i] )
end
end
end
end
-- Remove all Bindings from mapping
function keymap:unbind( map )
_map[map].keys = {}
_map[map].joy = {}
_keyLast = false
_keyCheck = false
_keyCheckFor = false
end
-- Binds the next key pushed to given mapping
function keymap:addBind( map )
_keyCheck = true
_keyCheckFor = map
end
-- KEYBOARD SUPPORT --
function love.keypressed( key , isrepeat )
if not _keyCheck then
local k = findMap("key", key)
if k then
if not isrepeat then
k.pushed = true
k.held = false
else
k.pushed = false
k.held = true
end
return
end
elseif key == "escape" then
_keyCheck = false
_keyLast = nil
_keyCheckfor = nil
else
print("key")
_keyLast = { "key" , key }
end
end
function love.keyreleased( key )
local k = findMap("key", key)
if not _keyCheck and k then
k.held = false
k.pushed = false
end
end
-- JOYSTICK SUPPORT --
function love.gamepadpressed( joystick, button )
local k = findMap("joy", button)
if not _keyCheck and k then
k.held = true
k.pushed = true
else
_keyLast = { "joy" , button }
end
end
function love.gamepadreleased( joystick, button )
local k = findMap("joy", button)
if not _keyCheck and k then
k.pressed = false
k.held = false
end
end
function love.joystickaxis( joystick, axis, value )
if not _keyCheck then
for i , m in pairs ( _map ) do
for j , k in pairs( m.joy ) do
if axis == 1 then
if k == "left" then
if value < -0.25 then
m.pushed = true
m.held = true
else
m.pushed = false
m.held = false
end
end
if k == "right" then
if value > 0.25 then
m.pushed = true
m.held = true
else
m.pushed = false
m.held = false
end
end
end
if axis == 2 then
if k == "up" then
if value < -0.25 then
m.pushed = true
m.held = true
else
m.pushed = false
m.held = false
end
end
if k == "down" then
if value > 0.25 then
m.pushed = true
m.held = true
else
m.pushed = false
m.held = false
end
end
end
end
end
else
if axis == 1 then
if value == -1 then
_keyLast = { "joy" , "left" }
elseif value < 1 then
_keyLast = { "joy" , "right" }
end
end
if axis == 2 then
if value == -1 then
_keyLast = { "joy" , "up" }
elseif value == 1 then
_keyLast = { "joy" , "down" }
end
end
end
end
return keymap
First off, copy KeyMap.lua into the root directory of your project.
At the top of your main.lua file, add in the following line of code:
Code: Select all
require "KeyMap"
Code: Select all
KeyMap:addMapping({"up","down","left","right","fire"})
If you know what KeyConstants you want to assign, we can go ahead and assign them manually like so:
Code: Select all
KeyMap:bind("up",{"key","w","up"})
The other way we can bind to a mapping is to call the pass in what mapping we want to bind to with the following function. It will take whatever key, button, or analog stick that is pressed.
Code: Select all
KeyMap:addBind("up")
Code: Select all
KeyMap:update(dt)
All that is left is to check if our bindings worked. Anywhere you need to check for keypresses, use the following line of code to check whether the button is down:
Code: Select all
KeyMap:isDown("up")
Now, what if we want to see if the button has just been pressed? Normally, we would need to check for that in love.keypressed(key,isrepeat), but now we just do:
Code: Select all
KeyMap:isPushed("up")
Code: Select all
KeyMap:isHeld("up")
I hope you all find this to be useful! I know I have
EDIT 01/15/14: Updated code to be an actual module rather than whatever it was before.