Sign in to follow this  
etothex

game input: key to actions

Recommended Posts

Here's my situation - I want to develop a robust input system that is fully customizable for my game. I think I will require: 1. The user can map any key to an action (right now I'm not worrying about combinations of keys) 2. The program has to be able to load and save the map on demand. 3. I think it would be best to plan for future internationalization, but I'm not worried about that now either. 4. It needs to be cross-platform. I'm using GLFW, but I would like to introduce DirectInput later for windows (even if only just making a std::map translating the DIK_* into GLFW_KEY_*) I was thinking that a std::map< int, boost::function > would work best, but I feel kind of clueless how to do the loading and saving. Do I make an enum of all possible actions, then keep a std::map< int, actions > around that I load and save, then build the std::map< int, boost::function > out of? It seems to me that it would be a pain: each time I wanted to add a new action, I would have to add a new entry in the enum as well as a new entry in the function which converts the enum to boost::function. Any ideas? How do other people do it?

Share this post


Link to post
Share on other sites
yeah, a simple input/function map. For saving and loading you likely need another map for string/function to serialize the actions. That sort of map can act as a registry so that you add functions via static trickery or some in code configuration.

Share this post


Link to post
Share on other sites
This is something I've been thinking about as well. Here's an idea I was thinking of for a configurable input system. Keep in mind, this idea is only for the immediate player, but could be easily extended (I hope) to work with AIPlayers and NETPlayers.

Since I need the input system to be able to remap key presses to certain actions, I had wondered about creating an enumeration of all the possible actions that can be performed in the game. Something like:


enum ActionList {
ACTION_MOVE_FORWARD,
ACTION_MOVE_BACKPEDAL,
ACTION_STRAFE_LEFT,
ACTION_STRAFE_RIGHT,
ACTION_JUMP,
ACTION_CROUCH,
ACTION_WEAPON_PRIMARY, // primary weapon attack
ACTION_WEAPON_SECONDARY, // in-case you allow secondary weapon attacks
// etc.
};



These actions would act as the values in an std::map. The key for the map you can set to the key/joystick/mouse button/axis. Now, because each input is used as the key in the map, it allows you to map more than one input to an action.

When you decide to go store the user's keymappings, you could use a user profile system that would store the user's keymappings in their own key config file. This would let multiple players play your game and each have their own customizable configurations.

Now, what I was thinking of doing, was having an input event notification system, where this system would add an input event to the event queue. If an event is found on the queue, then it would be processed, and passed off to an ActionMap system. The ActionMap system contains just what the name says; some sort of map/list/vector that contains all of the actions accrued during that frame. As each frame gets called, you could then have a function which processes each action in the list.

Something like this:


enum ActionList { ... }; // the actions from above

class ActionMap : public Singleton
{
private:
std::list<ActionList> m_actionList;

public:
void addAction(const ActionList& reAction);
void process();
};



The processing part, I haven't quite figured out how I had planned to handle it. Maybe you could have some sort of systems like ActionControllers that would modify the related object in some way.

Now, I'm certain there are design flaws in this; but, it's still something to get the creative juices flowing.

Share this post


Link to post
Share on other sites
if you examine the hl1 sdk you will find a set of flags that describe the key input actions

e.g.:
FIRE_BUTTON_PRESSED
FIRE_BUTTON_RELEASED
.....

you have 2 sets of flags
the current input, the last input and the incoming input

they check it each frame and change the states appropriately

the only downside is, you are limited to a certain number of commands

Share this post


Link to post
Share on other sites
Considering that there are only 256 Key combinations it's probably more efficient to use a static array (not vector) to map your keys instead of a STL map. You could define your actions as an enumeration (or an int if you need run time flexibility) so that your map has a small footprint. Also, saving an array of 256 elements to a file is dirt simple.

I have made such a system that also handled string input from the keyboard. Although if you want to do that with your system you will need a separate map for handling keyboard input. Mines was a 2 by 256 array so that the shift key and caps lock could be mapped to different parts of the array using little processor time.

I also worked a system that would let a player string together a series of key presses as most fighting games do. Although I have found that the best approach is to have a loop with a fixed frame rate.

Also, do note that tracking the changes in key states is necessary in all input processing with digital keys. I would suggest a set of bit flags (bit array? I have a mental block right now but I think the STL has a solution for this) to keep track of the state of each key.

Share this post


Link to post
Share on other sites
There would be tradeoffs to using a static array of 256 keys. The biggest one being that you don't need that much memory. With a list or map, you'll have at most, 10 inputs happening at once; since the average human only has 10 digits on their hands. Also, each frame, you'd need to find some way to cycle through the array of keys and check each one to see if it's been pressed. With the STL list, only when a key is pressed or released will it be registered with the input system. In that case, all you need to do is pop the first element off the list and process it. With this system, you could also create a "virtual" input system for your AI characters. Although, you would have to extend how that ActionMap class I spoke about works.

*shrug* Just a thought.

Share this post


Link to post
Share on other sites
Sounds like the event drive vs polling models.

The way I do it is to have a Trigger class and manager for firing game events when certain conditions are true. I derive an InputTrigger that takes an int and a functor as parameters, the index into the key state array from DirectInput and the input function to call. Each frame the trigger manager calls each trigger to test itself, and if the indexed key is pressed the input function is fired. I also derive triggers for keys released, mouse buttons etc. It's quite flexible really.

Share this post


Link to post
Share on other sites
My current/planned system:


// all possible keys
enum Key {
K_A, K_B,
K_1, K_2

K_ENTER, K_KP_ENTER,
K_L_SHIFT, K_R_ALT,


K_MB1, K_MB2,
K_MB3

,K_COUNT
,K_UNDEFINED
};

// a list of current keys
class KeyManager {
bool m_keys[K_COUNT];
bool m_oldKeys[K_COUNT];
public:
void setKey(Key p_key, bool p_state) {
m_oldKeys[p_key] = m_kets[p_key];
m_keys[p_key] = p_state;
}

bool getCurrentState(Key k);
bool getOldState(Key k);
};

class KeyState {
Key m_key;
KeyManager* m_keyManager;
public:
bool isUp();
bool isDown();
bool isPreviousUp();
bool isPreviousDown();
bool isAssigned();
void assign(Key p_key);
void unassign();
const char* getName();
};

class KeySetting {
KeyState m_primary;
KeyState m_secondary;
std::string m_name; // name of the setting
public:
bool isUp();
bool isDown();
bool isPreviousUp();
bool isPreviousDown();
bool isAssigned();
void assign(Key p_key);
void unassign();

// returns m_primary.getName()
// or "???"
// or m_primary.getName() + " or " + m_secondary.getName()
// depending on how the current assignment is
std::string getName();

std::string getAliasName(); // alias name is the name of the this setting
// see above on member doc.
};




This is roughly as far as I've come. The key setting will register itself within a KeySettingRegister(that will be able to save and load key settings, and perhaps used for setup later on).
Special keys are extended from the KeySetting, like

  • KeyActionDown
    execute() returns true if the key has recently been pressed ( isCurrentDown()&&isPreviousUp() )

  • KeyActionHold
    execute() returns true if the current key is down(isCurrentDown())



Later on I plan to add the ability to send time-updates to the KeySetting, so I can implement rapid keys(for smg's) among others.

Game code is done like this(in GameState code):
if( jumpKey.execute() ) {
player->jump();
}


hth

Share this post


Link to post
Share on other sites
Quote:
Original post by Dorvo
There would be tradeoffs to using a static array of 256 keys. The biggest one being that you don't need that much memory. With a list or map, you'll have at most, 10 inputs happening at once; since the average human only has 10 digits on their hands. Also, each frame, you'd need to find some way to cycle through the array of keys and check each one to see if it's been pressed. With the STL list, only when a key is pressed or released will it be registered with the input system. In that case, all you need to do is pop the first element off the list and process it. With this system, you could also create a "virtual" input system for your AI characters. Although, you would have to extend how that ActionMap class I spoke about works.

*shrug* Just a thought.


Chances are that you will use much more than 256 bytes using an STL list and you will definately have slower performance than array look up via an index. Using the STL (or any other dynamic container) for this would also involve the cost of repeated dynamic memory allocations and deallocations which has many negative affects on performance. When the max amouunt of RAM you'll ever need is that small, static memory allocations are better solutions.

Quote:
Original post by DorvoWith the STL list, only when a key is pressed or released will it be registered with the input system.

The same could be done with a static array if you choose to implement it that way.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this