What's the best way to implement rebindable keys?

Hi! I’m writing a little tetris clone to teach myself SDL, and I’ve run
across an interesting problem: what’s the best way to implement
rebindable keys?

You only need eight digital inputs to play Tetris (up, down, left,
right, spin-left, spin-right, start, and select) which I have in an
enum. My first attempt at keybinding was to have an array of keysyms
(int keysyms[8]), but that meant scanning the array every time I got a
keypress, which meant repeated code in every key-related or
joystick-related event.

My next attempt was just for the joystick-button binding - I made an
array as big as the number of buttons on the first joystick, and put one
of the Tetris keycodes into the entry for each button that was bound.
That worked, made a lot of code shorter and easier to read, but there’s
something like 350+ different keysyms that you can get events for, which
seems a little large just for keybinding.

Ideally, I’d like to use a hash of the event type and keysym or
button-number as an index into an array, but that seems a little complex.

How do you deal with keybinding in your SDL application?

Hi! I’m writing a little tetris clone to teach myself SDL, and I’ve run
across an interesting problem: what’s the best way to implement
rebindable keys?

“Best” is of course subjective, but I have a suggestion.

I created a concept of a binary, analog, and ballistics controller to
handle on/off style transitions, analog for continuous transitions, and
ballistics for difference of position reports. It can either be
on/asserted off/deasserted. I kept a key assignment table for every
controller in the game - a 1 to N mapping (a linked list). So in your
case, I’d create:

CTRL_UP
CTRL_DOWN
CTRL_LEFT
CTRL_RIGHT
CTRL_SPINLEFT
CTRL_SPINRIGHT
CTRL_START
CTRL_SELECT

as an enumeration of 0-7. Then create a structure containing the assigned
keys/buttons/whatever to them:

struct SControllerAssignment
{
EControllerType eType;
SDL_Key sKeyAssign;
struct SControllerAssignment *psNextController;
}

So in memory, it’d look something like (forgive the names and whatnot -
I know they probably aren’t right):

CTRL_UP -> SDL_KEYUP -> SDL_PAD8
CTRL_DOWN -> SDL_KEYDOWN -> SDL_PAD2

That way you can add/take away assignments from anywhere. Of course you
can extend the SControllerAssignment structure to include make/break from
any other sources - including joysticks.

(int keysyms[8]), but that meant scanning the array every time I got a
keypress, which meant repeated code in every key-related or
joystick-related event.

That’s not a bad thing. Scanning each key for all assignments when an
event comes in (you are scanning only when those types of events are
deposited in the queue, right?) does not take much CPU power.

Ideally, I’d like to use a hash of the event type and keysym or
button-number as an index into an array, but that seems a little complex.

It is. You can’t get around looking to match up logical controllers within
a game to physical controllers. Basically I created a logical<->physical
converter that can accept anything. I do have to scan the active
controller list looking for its assignment.

This has the distinct advantage of being very flexible - as well as not
having to keep a keymap on all held keys. Plus, that means that you can
assign one function to multiple controllers without having one override
another continuously due to polling.

–>Neil-------------------------------------------------------------------------------
Neil Bradley What are burger lovers saying
Synthcom Systems, Inc. about the new BK Back Porch Griller?
ICQ #29402898 “It tastes like it came off the back porch.” - Me

Tim Allen wrote:

Hi! I’m writing a little tetris clone to teach myself SDL, and I’ve run
across an interesting problem: what’s the best way to implement
rebindable keys?

You only need eight digital inputs to play Tetris (up, down, left,
right, spin-left, spin-right, start, and select) which I have in an
enum. My first attempt at keybinding was to have an array of keysyms
(int keysyms[8]), but that meant scanning the array every time I got a
keypress, which meant repeated code in every key-related or
joystick-related event.

My next attempt was just for the joystick-button binding - I made an
array as big as the number of buttons on the first joystick, and put one
of the Tetris keycodes into the entry for each button that was bound.
That worked, made a lot of code shorter and easier to read, but there’s
something like 350+ different keysyms that you can get events for, which
seems a little large just for keybinding.

Ideally, I’d like to use a hash of the event type and keysym or
button-number as an index into an array, but that seems a little complex.

It isn’t all that complex. I’ve written several applications that had
rebindable keys. I used a simple vector of function pointers indexed by
keysym. Most of the entries vector in the vector point to a do nothing
routine so that invalid keys are dropped quickly and without any special
case code. The entries for bound keys point to the function that does
the operation you want. This approach also lets you have multiple
bindings for actions, i.e. more than one key can perform the same action.

When you are handling multiple key sequences just have multiple tables.
The first key selects the second table, and so on until you hit an
action routine that resets the table index and performs your action or
an ignore function that just resets to the first table.

You will want to use separate tables for keysyms and events just so you
can be absolutely sure not to confuse a keysym for an event and vice versa.

	Bob Pendleton-- 

±-----------------------------------+

I had a similar setup to this one, but I also wanted to make the
keys/buttons as configurable as possible. So I did something like this:

  • Enum a set of function names, in your case, maybe something like this:

{ACTION_UP, ACTION_DOWN, ACTION_LEFT, ACTION_RIGHT, ACTION_SPIN_RIGHT,
ACTION_SPIN_LEFT, ACTION_PAUSE, ACTION_SELECT, ACTION_LAST}

  • Then create an array like this:

Uint8 KeyMap[SDLK_LAST]; // 350 bytes or so shouldn’t be too wasteful.
Uint8 ButtonMap[MAX_BUTTONS]; // MAX_BUTTONS should be set to the number
// of buttons on your joystick (optional)

  • In your initialization or loading of config settings, you can assign
    certain keys to have certain functions:

KeyMap[SDLK_ESCAPE] = ACTION_PAUSE;
KeyMap[SDLK_UP] = ACTION_UP;
ButtonMap[9] = ACTION_PAUSE;

  • You can also create a ‘virtual controller’ which is just an array of
    button states:

Uint8 InputDev[ACTION_LAST]; // Number of buttons your game recognizes

  • In your game loop when you handle keyboard and joystick events, you
    update your controller state:

if Key down:
InputDev[KeyMap[]] = 1;

if Key up:
InputDev[KeyMap[]] = 0;

if Button down:
InputDev[ButtonMap[]] = 1;

if Button up:
InputDev[ButtonMap[]] = 0;

(Axis-checking is the usual code if you’re using a joystick)

  • When the game needs to know whether the ‘up’ button or key is pressed,
    you can access InputDev[ACTION_UP], which will be true or false.

All of that stuff could be put in a class and wrapped up more nicely, or
just written better altogether, but it solved a lot of my problems. It
also makes it possible to have several keys perform the same function.
Maybe some players want to select things with ‘Enter’ and some like to
use the space bar. Setting both keys to the same ACTION* value will do
that. It also makes it easier to configure your buttons, since you can
load and set your KeyMap/ButtonMap values from a config file at startup.

Far from the ‘best’ way, I’m sure, but it does the job.

RyanOn Mon, 9 Sep 2002, Tim Allen wrote:

Hi! I’m writing a little tetris clone to teach myself SDL, and I’ve run
across an interesting problem: what’s the best way to implement
rebindable keys?

You only need eight digital inputs to play Tetris (up, down, left,
right, spin-left, spin-right, start, and select) which I have in an
enum. My first attempt at keybinding was to have an array of keysyms
(int keysyms[8]), but that meant scanning the array every time I got a
keypress, which meant repeated code in every key-related or
joystick-related event.

My next attempt was just for the joystick-button binding - I made an
array as big as the number of buttons on the first joystick, and put one
of the Tetris keycodes into the entry for each button that was bound.
That worked, made a lot of code shorter and easier to read, but there’s
something like 350+ different keysyms that you can get events for, which
seems a little large just for keybinding.

Ideally, I’d like to use a hash of the event type and keysym or
button-number as an index into an array, but that seems a little complex.

How do you deal with keybinding in your SDL application?


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl