How to stop capturing Keyboard event until current keyboard event finish?

I’m having problem with keyboard event when pressing the key before last keyboard event finish being executed. Here I have a onKeyPressed event:

keyBoardListener->onKeyPressed = 
[&](cocos2d::EventKeyboard::KeyCode keycode, Event* event){
    switch (keycode)
      case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
      // Move 900 pixel to the Right in 3s
      case cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW:
      // Jump.
};

Lets say my character is now at position x When I press KEY_RIGHT_ARROW, my chracter start moving 900 pixel to the right in 3 seconds. However, 1 second after that I press KEY_UP_ARROW. Now I expect that my character keep moving until he reaches the goal (x + 900) then jump, but in fact he jumps right where he was when I press KEY_UP_ARROW.

The 2 solutions that came out of my mind is that I need to prevent my app from capturing any new Keyboard event until the current event finish or save them somewhere and execute them later in a First In First Out sequence after the last one finish. So:

1/ Is that a good way to deal with it? If so then how to do it ?

2/ Are there any other solutions ?

Your help is very much appreciated.

Question edited to improve explanation.
Update : I tried to set a flag to prevent new keyboard event but It still the same:

int flag = 0;
keyBoardListener->onKeyPressed = 
[&](cocos2d::EventKeyboard::KeyCode keycode, Event* event){
    if(flag==0)
    {
       flag++;
          switch (keycode)
             case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
             // Move 900 pixel to the Right in 3s
             case cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW:
              // Jump.
       flag--;
    }
};

Your flag really is not doing anything.

There are a few techniques. Two of the concepts I know are using state flag per key and then the other is queueing up events over time as an input buffer.

These are just examples that likely will need to be expanded upon for your specific game to ensure correct behavior.

The concept that you’re missing is that the keyboard events don’t fire every frame so you need a way to detect that a key is down while still also moving the character every frame update.

// As you've tried to do, but you're not keeping the state around long enough for any effect
// globally accessible state - some prefer to handle all input in a single place, otherwise ...
// you can create a listener on every class Node that only checks for keys that matter to itself
bool keystate[256]; // false = up, true = down
listener->onKeyPressed = [&](...) { 
   states[keycode] = true;
}
listener->onKeyReleased = [&](...) { 
   states[keycode] = false;
}
// game update loop update
if ( states[KEY_RIGHT_ARROW] ) {
  // move at rate of 300px/s
} else if ( states[KEY_UP_ARROW] ) {
  // Jump
}

Input event queue is more complex, and technically is already being done by the operating system (and accessed through GLFW which passes on through cocos2d-x’s event listener dispatch). You can still add a queue on top for abstracting things like de-bouncing, combos, multi-key-press.

1 Like

For those who have the same problem, @Makalele guide me with another solution, which stores all actions into a vector and execute them 1 by 1 here :

Hi @stevetranby, I’ve tried your advice but there’re something not working as expect.
1st: If I check for the keyState in update() function, my moveTo action in the if condition called many times just with 1 press in stead of just once as I expect. I think the problem caused by multiple call for action as long as I hold the key through every frame.
2nd: new keyboard event still able to interrupt in processing keyboard event instead of being denied until current keyboard event finish as expect .
How should I solve these problem? Thank you very much :slight_smile:
Here is my event Listener:

keyboardListener->onKeyPressed = [&](cocos2d::EventKeyboard::KeyCode keyCode , Event* event) {
		switch (keyCode)
		{
		case cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW:
			keyState[(int)keyCode] = true;
			break;
		case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
			keyState[(int)keyCode] = true;
			break;
		case cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW:
			keyState[(int)keyCode] = true;
			break;
		}
	};
	keyboardListener->onKeyReleased = [&](cocos2d::EventKeyboard::KeyCode keyCode, Event* event) {
		switch (keyCode)
		{
		case cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW:
			keyState[(int)keyCode] = false;
			break;
		case cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
			keyState[(int)keyCode] = false;
			break;
		case cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW:
			keyState[(int)keyCode] = false;
			break;
		}
	}; 

Here is my update function:

void TestScene::update(float delta)
{
	if (keyState[(int)cocos2d::EventKeyboard::KeyCode::KEY_LEFT_ARROW])
	{
		mUFO->runAction(createMoveBackAction());
	}
	else if (keyState[(int)cocos2d::EventKeyboard::KeyCode::KEY_RIGHT_ARROW])
	{
		mUFO->runAction(createMoveToAction());
	}
	else if (keyState[(int)cocos2d::EventKeyboard::KeyCode::KEY_UP_ARROW])
	{
		mUFO->runAction(createJumpAction());
	}
}

Actions are probably the wrong tool to use here, unless you want the actions to finish before another can start?

Regardless here are a few other things to note:

First, always try and recognize duplicated code. You can remove most of your key handlers, unless this sample isn’t representative of what you’re looking for.

keyboardListener->onKeyPressed = [&](cocos2d::EventKeyboard::KeyCode keyCode, Event* event) {
    keyState[(int)keyCode] = true;
};
keyboardListener->onKeyReleased = [&](cocos2d::EventKeyboard::KeyCode keyCode, Event* event) {
    keyState[(int)keyCode] = false;
};

Second, when you want to run different Actions on a node you usually will want to (or need to) stop the previous action(s). You can do this “with a hammer” by calling mUFO->stopAllActions(); or “with a scalpel” by setting the action’s tag, saving that tag, and calling mUFO->stopActionByTag(tag);.

Third, you probably only want to stop and create/run the new action only if the current action is not the same one, so if RIGHT is pressed and the current action is LEFT then you should exec the stop, run action code. However, if UP is pressed (down, state[] is true) and the current action is JUMP then you probably want to not do anything.

Note, if you’re trying to make a platformer I would strongly recommend updating position (and any other node property) directly instead of through Actions. auto p = mUFO->getPosition(); p.x += speed * dt; mUFO->setPosition(p); as simplistic example. You’ll also have to check for collisions, key state changes (like you’re doing now), etc.

A google search for “platform game development tutorial” (or whatever movement genre you’re working on) would probably be a good start.