2
\$\begingroup\$

I'm looking for a way to retrieve the last key pressed even when other keys might be pressed for a top-down four-directional game. For example:

  • W is pressed → Move up
  • D is pressed → Move right
  • S is pressed → Move down

The above I can get to work fine using a stack. When S is released, then we start moving right again, and when D is released, we go back to moving up. The issue arises when trying to release (say W) and then pressing W again to move up before S and D are released.

I've tried using arrays and lists to solve this problem (to use flags or remove a key from any point in the collection), but it results in whatever key is last in the if statements to check for the movement keys being given the priority.

Any help is greatly appreciated. If there's anything else I could provide please let me know.

Edit - a more detailed example:

  • Player is currently standing still.
  • User presses W to move the player up. This key is stored as the current movement key on a stack and as long as the key is pressed (checking per frame), the player will continue to move up.
  • User presses D to move the player right. The D key is stored as the current movement key on the same stack W was stored on. W is not the current movement key anymore.

  • User releases the W key - since this is stored on a stack, we know W was released but we cannot remove it from the stack (but we do know it is on the stack). I do not want to just keep adding the keys to the stack, thus the reason we check if the stack contains the key.

  • User presses the W key again. Since D is still the current movement key, and since we weren't able to remove W from the stack, we cannot add it to the stack again.
\$\endgroup\$
  • \$\begingroup\$ What is the desired behaviour? For example, in a pacman-maze game, the player may press keys (or thumbstick) to move diagonally to anticipate or improve cornering gameplay. Instead of putting the keystrokes in a stack, you may instead anticipate what the player's intentions are based on context. So if the corridor is vertical, a diagonal movement means the player plans to hit the next horizontal corridor. So you may rethink your problem and see if you can approach it based on the player's desired movement instead? \$\endgroup\$ – Felsir Jan 1 '16 at 10:05
  • \$\begingroup\$ Open-world behavior; there aren't any "boundaries" per se, so that wouldn't quite work. \$\endgroup\$ – Sheer Jan 1 '16 at 13:31
  • \$\begingroup\$ Okay, so if I understand correctly, the desired behavior is like: [W] is pressed, player moves up. [W] is still pressed, but [D] is also pressed, player changes direction to right. So you should remember the direction the player is travelling now- and then check if the player wants to change direction. Only continue the direction if the current key pressed is equal to the current direction. Would that work? \$\endgroup\$ – Felsir Jan 1 '16 at 16:01
  • \$\begingroup\$ I added a more detailed scenario to help clarify what I think you're trying to get at. \$\endgroup\$ – Sheer Jan 1 '16 at 17:10
  • \$\begingroup\$ The added clarification shows your stack mechanism. What I'm curious about is what the user expects to happen. From your description it is not clear to me why you actually have a stack for the keypresses. \$\endgroup\$ – Felsir Jan 1 '16 at 17:33
1
\$\begingroup\$

You want to know if the pressed keys have changed. If no new keys have been pressed or released you don't need to update the direction.

You could implement this:

  1. Use a bitmask for the directions 1=up, 2=down, 4=left and 8=right. If the sum of the keys pressed is different than the sum you stored in the last frame, some logic needs to happen.

  2. Now, clear the list of directions. Add directions to the list.

  3. If the list is empty, the movement stops.

  4. If the list contains only one element it can mean two things:

    1. The player changes direction or
    2. The current direction is the only direction pressed.
  5. If more than one element is in the list, remove the current direction (if that one is in the list).

  6. The first in the list is the new direction.

  7. We keep that direction until our 'change' is triggered because the bitvalue changed.

Now this is how it should work. Our bitvalue is 0. The player stands still.

The user presses W the bitvalue becomes 1, which is different from 0. We create a list thats contains only {W}. This becomes our new player direction. We store the bitvalue 1.

For a couple of frames the key pressed is W so the bitvalue of 1 is equal to the stored value. We keep going up.

The user now presses W and D. The bitvalue is 9 which is different than our stored 1. The list contains {D,W}. It is more than one element, so we remove the current direction W. The new direction is the first element in the list: D we also store value 9.

The bitvalue stays 9 for a couple of frames.

The user now releases W. The bitvalue is 8 which is different from 9. The only key pressed is D so we continue that direction. 8 is stored.

The user releases all keys. The bitvalue is 0 which is different from 8. The list is empty and the player stops. 0 is stored.

(Sorry no code example, I'm writing this on an ipad, typing code is a pita on the onscreen keyboard...)

\$\endgroup\$
  • \$\begingroup\$ Unless I'm missing something, this doesn't seem to account for if, say, W, D, and A are all pressed? \$\endgroup\$ – Sheer Jan 2 '16 at 21:10
  • \$\begingroup\$ It does, unless they are all pressed (for the first time) at the exact same frame. In that event the order you fill the list determines the direction. Also think about this, when would a player press opposite directions at the same time? This code will achieve what you're actually looking for: a good fluid control scheme for your user. \$\endgroup\$ – Felsir Jan 2 '16 at 21:30
  • \$\begingroup\$ In answer to your question - in the instance a player presses A while releasing D; if there are a few frames where they're still lifting their finger off D and also pressing A, I don't want the player to just stop moving (result is jagged movement). \$\endgroup\$ – Sheer Jan 2 '16 at 21:36
  • \$\begingroup\$ Well that is solved by this code. This is three 'events': D is pressed (bit=8). D and S are pressed (bit=12) and only S is pressed (bit=4). The first results in movement to right, second event changes direction, the last event keeps the direction. No jagged movements. \$\endgroup\$ – Felsir Jan 3 '16 at 7:58
0
\$\begingroup\$

If you are looking for the last key pressed, make four booleans and very simply change any boolean(based on what key is pressed) to true and rest to false. This way you can add a method for handling the booleans and then do the action. To do it after you released the new key pressed, you have to check that the keys arent pressed(dont know how to do that in C#). Also add two sets of four booleans one for what just got pressed(e.g. W) and what got pressed BEFORE you pressed W(e.g. D)

\$\endgroup\$
  • \$\begingroup\$ That still results in the last key in the block of if statements (checking WASD) given the highest priority. I already tried that implementation prior to my post; thank you though. \$\endgroup\$ – Sheer Jan 1 '16 at 13:37
0
\$\begingroup\$

I'm not sure why but for some reason a solution I tried early on is working as I wanted it to now.

The implementation's psuedo-code is as follows:

// Initialize a list for movement keys and add the first element to be "No Key".
// For MonoGame, this is Keys.None.

/* In the update loop */
Check current keyboard state.
If (W key is pressed and the movement key list doesn't contain W)
    Add W to the list of movement keys.

// Repeat above for S, D, and A

If (W key is released and the movement key list contains W)
    Remove W from the list of movement keys.

// Repeat above for S, D, and A.

To get the current movement key, I just check movementKeyList[movementKeyList.Count - 1].

Incredibly simple and stupid and I'm a bit agitated that I missed something before to cause me this much trouble, but 'tis the life of programming. Thank you all who helped!

\$\endgroup\$
  • \$\begingroup\$ Glad you found a solution you're happy with :-) \$\endgroup\$ – Felsir Jan 3 '16 at 8:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.