Dealing with opposite keys pressed at the same time

Hi,
I have a character that by default has a idle animation. When pressing the right arrow it moves to the right and triggers a walk cycle animation. And to the left, the same cycle with a scalex -1.
When any arrow key is released i run the idle action again. The problem is that if I press both left and right arrowsat the same time, and then release the right one,the sprite moves to the left, but with the idle animation, because it is the one triggered when releasing any key.

Any idea how to deal with this situation where opposite keys are pressed and the one of the two is released?

Not sure how you’re handling the movement, since it sounds like that’s working correctly, but maybe If you track two bool flags for direction. You can name these whatever, and you should only use this as a possibility and reference, not a solution.

(off my memory, and untested, could be wrong)


// track input state
bool keyLeftArrowDown;
bool keyRightArrowDown;

// on pressed/down for each key
onPressed(Left) -> { keyLeftArrowDown = true; updateState(); }
onPressed(Right) -> { keyRightArrowDown = true; updateState(); }
onReleased(Left) -> { keyLeftArrowDown = false; updateState(); }
onReleased(Right) -> { keyRightArrowDown = false; updateState(); }

// updateState()
if (keyLeftArrowDown || keyLeftArrowDown) { 
  // if changed, update animation (e.g. was right, now left)
} else { 
  // both are released, so we can do idle animation here
}

You may need to track when keyDown flag is changed, so you know the frame when it went from released->pressed, or vice-versa. To determine if animation should change, for example, you may need to track previous/current animation, or even previous left/right arrow flag state (e.g. keyLeftArrowDownPrevious, or something).

Anyway, just some thoughts to hopefully steer you toward a solution.

I see. Let me share an extract of my code, because I think I am not doing it right

//...more code

    //Create keyboard event listener to move sprite with AD
    auto eventListenerKeyboard = EventListenerKeyboard::create();

    eventListenerKeyboard->onKeyPressed = [&](EventKeyboard::KeyCode code, Event* event) {
        // . run constantly translation
        keys[int(code)] = true;
        // . run action once
        switch(code){
            case EventKeyboard::KeyCode::KEY_D:
				sprite->stopAllActions();
				sprite->setScaleX(1);
				sprite->runAction(walkRigthAction);
                break;
			case EventKeyboard::KeyCode::KEY_A:
				sprite->stopAllActions();
				sprite->setScaleX(-1);
				sprite->runAction(walkRigthAction);
                break;
            default:
                break;
        }
    };

    eventListenerKeyboard->onKeyReleased = [&](EventKeyboard::KeyCode code, Event* event) {
        // . run constantly translation
        keys[int(code)] = false;
        // . run action once
        switch(code){
            case EventKeyboard::KeyCode::KEY_D:
				sprite->stopAllActions();
				sprite->setScaleX(1);
				sprite->runAction(idleAction);
                break;
			case EventKeyboard::KeyCode::KEY_A:
				sprite->setScaleX(-1);
				sprite->stopAllActions();
				sprite->runAction(idleAction);
                break;
            default:
                break;
        }
    };


    this->_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListenerKeyboard, sprite);


    // Run update schedule
    this->scheduleUpdate();

//...more code

Basically my logic is to stop all actions when pressed as the firts step to then run the one corresponding with the the key pressed. And then in the key released stop all actions again and set it back to the idleAction. This works perfectly as long as I don’t press A and D at the same time.
Is my logic incorrect according to that code?
Here is the result. At the end, the moment I press A and D and release one of them, it moves in the right direction, but with the idle animation.
Animation

This might work for this specific test case, just choose to stop+rightAction or stop+idle depending on if the other key is still down?

        switch(code){
            case EventKeyboard::KeyCode::KEY_D: {

// Do they still want to go left? else, idle
sprite->stopAllActions();
if (keys[KEY_A]) { 
  sprite->setScaleX(-1);
  sprite->runAction(walkRight);
} else { 
  sprite->runAction(idleAction); // setScale if needed
}
                } break;
			case EventKeyboard::KeyCode::KEY_A: {

// Do they still want to go right? else, idle
sprite->stopAllActions();
if (keys[KEY_D]) { 
  sprite->setScaleX(1);
  sprite->runAction(walkRight);
} else { 
  sprite->runAction(idleAction); // setScale if needed
}

                } break;
            default:
                break;
        }

However, you may want to look into managing movement and animation more directly and at a “lower level” instead of using Actions. You’d just do position = position + velocity or whatever. Then set animation whenever velocity changes.

// in constructor / initialization
// would make this class instance field instead?
Vec2 prevVelocity = Vec2::Zero; 
// in update method
Vec2 position = getPosition();

// get 'velocity' based on keys down (Key_A has priority)
if (keys[KEY_A])      { velocity = Vec2(-1,0); }
else if (keys[KEY_D]) { velocity = Vec2(1,0); }
else                  { velocity = Vec2::Zero; }

// did velocity change? if yes, start new animation
if (velocity != prevVelocity) {  
  if (velocity.x > 0) { /* start right animation */ }
  if (velocity.x < 0) { /* start left animation */ }
  if (velocity.x == 0) { /* start idle */ }
}

prevVelocity = velocity; // store for next update
Vec2 newPosition = position + velocity;
// could check collisions here (ie: only set if new position is walkable, etc.)
setPosition(newPosition);

Anyway, just some quick thoughts.

Thanks @stevetranby , the first option you suggested worked. I did have to include the idle action in the pressed events to, so if both D and A are pressed it goes into idle (other wise it would be walking on without moving).
I will defineltly try out the second option too base one velocity. It looks quite interesting and less convoluted.

Cheers

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.