taking input too fast

Started by
7 comments, last by swiftcoder 19 years, 7 months ago
hi, i seem to come across this common problem, which i "hack" my way around. im wondering if there is a general solution, or what im doing is totally wrong, or what. anyway, im using SDL for input, and i have this problem. ill use the left mouse button as example, but it happends with all keys or mouse buttons. -check directly if the left mouse button is pressed -if so, do something -the next frame comes, and that flag is still set that the mouse button is pressed, even though i only clicked it once, getting 30 or 800 FPS will register a mouse click over several frames -the mouse state is still registerd as being pressed down, so all of a sudden im clicking something i shouldnt be clicking. i found there are 2 ways around this. the first only allows the button to be pressed a single time, evne if its held down forever. the code looks like this:


static bool pressed = false;

if( some button is pushed)
{
     if(!pressed) 
         DO_SOMETHING_HERE();

     pressed = true;
}
else pressed = false;
this basically makes it so input is only taken that single time. the other way i do it is using a delay. i mainly need this in 2 cases, 1) i want the user to be able to hold down the botton, just not so fast as SDL can take it, and 2) i have a situation where its taking the input too fast in 2 different pieces of code. basically it looks something like...

static Uint32 delay = SDL_GetTicks();

if(some button is pushed)
{
     if(SDL_GetTicks() - delay > 200)
         DO_SOMETHING_HERE();
 
     delay = SDL_GetTicks();
}
this only takes the input every 200 MS. anyway, these are both ugly hacks, and i dont like them. im having some weird input problems pop up in my code that i cant figure out and no one seems to have experianced before. mainly, for some reason, at some point, my mouse button gets stuck down. like its as if im pushing the mouse button, but im not. it doesnt happen alot, but it does happen. i notice that if i click somewhere, anywhere, it goes away. i also noticed that i cant click and drag the window while this is happening. pretty weird. i posted about my input problem on the SDL newsgroup, and the guy was wondering why i take input directly. he says i should just take it while polling. i dont get it! how could you take ALL the input for your entire game in your Polling() loop? how is that even possible? wouldnt it be a big sloppy mess? not to mention it would break encapsulation, like moving the Player for example, you would then need to move him from your input polling code, instead of inside the Player class somewhere. does anyone do this, or is this guy crazy? thanks a lot for any help!
FTA, my 2D futuristic action MMORPG
Advertisement
I don't know how SDL deals with input, but the situation you are describing is very common. Generally, it is handled something like this:
down = get_input();             // assuming 1 means down and 0 means upup = ~down;                     // 1 means up and 0 means downchanged = down ^ previous_down; // 1 means changed up or downpressed = down & changed;       // 1 means pressed this framereleased = up & changed;        // 1 means released this frameprevious_down = down;           // save for next frame

This is much like your first example. Note some of the lines above are not always needed.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
When you take input, check to make sure you didn't do SOME_THING last frame also. That's all.

As far as the input OOPing, basically, what you want to do is have a class that encapsulates an action or a message like MOVE_LEFT or JUMP or whatever. So each frame, you loop through all the queued SDL events and process each one. If you get an SDLLEY_ENTER key pressed for example, and you want to shoot your gun, you create a new gun firing event object and send it somewhere else (either to the player or an intermediate class that knows about the player) or just "activiate it":

ActionClass action = ActionFireGun( some parameters );

SomeGlobalActionHandler.Queue( action );

-or-

Player.RespondToEvent( action );

or for polymorphic goodness, just try:

action.Act();

and just let it figure out what to do on it's own! ... or something sorta like that. The nice thing about encapsulating the action itself in an object is that you can fire off these objects from anywhere and let them figure out what to do without your knowledge. For instance, you could register an action object with a button press and the button will have no idea what the action it contains actually does. It shouldn't matter either. This approach lets you rearrange the button config and also trigger these actions from other sources as well. With the gun firing example, you can have the letter F, ENTER, and right mouse click all fire the gun. All they really do is fire off the same action object though (however you choose to implement that).
^^^ dangit, that was me.
hmmmm... so you think i should set up some sort of event handling system instead of taking input directly? it seems like a lot of work compared to just doing if(keys[SDKL_whatever]), you know?

but, is it sloppy take input directly? it just seems like there are a million places where i take input, so it would be a lot of work if each time i wanted to add something that was effected by input, i had to go and make another enum or whatever.

hey John, sorry but i didnt really understand that. i guess i have to freshen up on the bitwise operations.

thanks for anymore help.

FTA, my 2D futuristic action MMORPG
Here's what I have come up with and I 'm pretty happy with it, finally.
I will only talk about the keyboard but the same applies for the mouse or whatever input device you 'd like.
I have a IInput interface and a DXInput class deriving fron IInterface (I can make a SDLInput class or whatever as long as it derives from IInput).

At the start of each frame I "poll" the status of the keyboard and keep it in a integer array. Let's say it's declared as "int m_Keys[256]".
I also keep another array handy to store the states I 'm interested in. *Not* the states as returned by polling the device, but my own states (int m_States[256]).

After I poll the keyboard state, I run a loop through all 256 elements of m_Keys and by checking it's current status with its previous frame's state I come to some very nice conclusions about the relative state changes, like if the key has just been pressed, or has just been released etc. The most interesting part is that using some bit manipulation, I "flag" this state in the states array.
If it seems confusing, here's example pseudocode:

// some flags for m_States#define RELEASED 0x00#define JUST_PRESSED 0x01#define JUST_RELEASED 0x02#define PRESSED 0x04for (int i = 0; i < 256; ++i){	if (m_Keys)	{		// the key was "polled" as "pressed"; let's check it out		if (m_States & PRESSED == 0) // this key was *not* pressed last frame		{			// see? not only it's marked "pressed" but "just pressed" too...			m_States = PRESSED | JUST_PRESSED;		}		// similar logic follows for other states		else if (...)		{		}		else if (...)		{		}	}}


See what's going on here? I only have to use IInput::Key(int key, int flags) to check a key out. E.g input->Key(SDLK_C, JUST_PRESSED) will return true only if the "C" key was *just* pressed.
I have gone a step further and have added some timing and generate KEY_REPEAT too (for text input in my GUI classes). The point is you can add whatever flags in the m_States array. Anything that helps you in your game...

You can use this input handling class as a singleton or pass it as a parameter to your ProcessInput() function.

That's it. If something seems confusing I 'd be glad to clarify it.

HTH,
Yiannis.

//to simplify:

bool key_down[256]
bool key_just[256]

//each frame

for(int i=0;i<256;i++){

key_just = false;

if (key_down != REALKEYTESTER){
key_down = REALKEYTESTER;
key_just = true;
};

};

//REALKEYTESTER is the way you find out if a key was pressed!
//remember to clear everything at start!

//now you can easely test for:

bool KeyDown(int i){return key_down;};
bool KeyUp(int i){return !key_down;};
bool KeyJustDown(int i){return key_down && key_just;};
bool KeyJustUp(int i){return (!key_down) && key_just;};

//the KeyJustDown is what you look for if you want something to
//happend when you push the key...
//windows buttons (GUI) only reacts when you release the mouse,
//KeyJustUp()

//i also implemented a timer, counting when key was down, and
//zeroing when key is up, then you can set a limit to 0.5 secs
//for holding the button down (scrolling faster trough a list maybe)
-Anders-Oredsson-Norway-
The solution to the problem is called debouncing, just set a flag in 1 frame to true when something is pressed, and if in any of the following frames the button is released turn the flag off. if the flag is true in the next frame just dont do anything.

So only act on the buttong when the flag is true and the button has been pressed.

ace
This is the method I use, it works very well in most situations:

class Input{protected:int keyCount;unisgned char *keys, oldKeys;public:Input();	bool inline curKey(int index) { return keys[index] != 0); }	bool inline oldKey(int index) { return oldKeys[index] != 0); }	// Utility functions	bool inline keyDown(int index)	{ return ( curKey(index))&&(!oldKey(index)); }	bool inline keyStillDown(int index) { return ( curKey(index))&&( oldKey(index)); }	bool inline keyUp(int index) { return (!curKey(index))&&( oldKey(index)); }	bool inline keyStillUp(int index)	{ return (!curKey(index))&&(!oldKey(index)); }}Input::Input(){	unsigned char *tempKeys=SDL_GetKeyState(&keyCount);	keys = malloc(sizeof(unsigned char) * keyCount);	memcpy(keys,tempKeys,sizeof(unsigned char)*keyCount);	oldKeys = malloc(sizeof(unsigned char) * keyCount);}


Then every time through the event loop, you call this function of Input:

void Input::Update(){	SDL_PumpEvents();	memcpy(oldKeys,keys,sizeof(unsigned char)*keyCount);	unsigned char *tempKeys = SDL_GetKeyState(&keyCount);	memcpy(keys,tempKeys,sizeof(unsigned char)*keyCount);}


[edit] credit SuperPig with this code, I shamelessly copied it from the enginuity articles ;)[/edit]

Hope this helps,

SwiftCoder

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This topic is closed to new replies.

Advertisement