2
\$\begingroup\$

I'm using a simple InputManager class for an XNA game with keyboardState and lastKeyboardState that gets updated and has KeyDown/KeyReleased/KeyPressed functions... but ran into a small problem:

I have the 'Enter' key being used to select a tile on the map, which brings up a menu to build something on that tile... but once the menu is up, then it's updating to check whether the Enter key has been pressed to select a menu option. So the logic ends up something like this: IsKeyPressed(Keys.Enter) is true -> Open Menu -> IsKeyPressed(Keys.Enter) still true -> Menu Option 1 selected. Obviously, I don't want the game to automatically select whatever the first menu option is. Sooo, I thought maybe I could just 'flush' the old KeyboardState for one frame after IsKeyPressed so it can't still be true when the menu pops up... but apparently a KeyboardState isn't nullable.

Any ideas as to how I could flush the lastKeyboardState?

Here's the class:

using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Your_Quest.GameEngine
{
    public class InputManager : Microsoft.Xna.Framework.GameComponent
    {
        #region Variables

        static KeyboardState keyboardState;
        static KeyboardState lastKeyboardState;

        static GamePadState[] gamePadStates;
        static GamePadState[] lastGamePadStates;

        #endregion

        #region Properties

        public static KeyboardState KeyboardState
        {
            get { return keyboardState; }
        }

        public static KeyboardState LastKeyboardState
        {
            get { return lastKeyboardState; }
        }

        public static GamePadState[] GamePadStates
        {
            get { return gamePadStates; }
        }

        public static GamePadState[] LastGamePadStates
        {
            get { return lastGamePadStates; }
        }

        #endregion

        #region Constructor

        public InputManager(Game game) : base(game)
        {
            keyboardState = Keyboard.GetState();

            gamePadStates = new GamePadState[Enum.GetValues(typeof(PlayerIndex)).Length];

            foreach (PlayerIndex index in Enum.GetValues(typeof(PlayerIndex)))
            {
                gamePadStates[(int)index] = GamePad.GetState(index);
            }  
        }

        #endregion

        #region Methods

        public static void Update()
        {
            lastKeyboardState = keyboardState;
            keyboardState = Keyboard.GetState();

            lastGamePadStates = (GamePadState[])gamePadStates.Clone();
            foreach (PlayerIndex index in Enum.GetValues(typeof(PlayerIndex)))
                gamePadStates[(int)index] = GamePad.GetState(index);
        }

        public static bool KeyReleased(Keys key)
        {
            return keyboardState.IsKeyUp(key) && lastKeyboardState.IsKeyDown(key);
        }

        public static bool KeyPressed(Keys key)
        {
            return keyboardState.IsKeyDown(key) && lastKeyboardState.IsKeyUp(key);
        }

        public static bool KeyDown(Keys key)
        {
            return keyboardState.IsKeyDown(key);
        }

        public static bool ButtonReleased(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonUp(button) && lastGamePadStates[(int)index].IsButtonDown(button);
        }

        public static bool ButtonPressed(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonDown(button) && lastGamePadStates[(int)index].IsButtonUp(button);
        }

        public static bool ButtonDown(Buttons button, PlayerIndex index)
        {
            return gamePadStates[(int)index].IsButtonDown(button);
        }

        private static void Flush()
        {

        }

        #endregion
    }
}

Note: Ideally, I'd like to be able to just use KeyPressed 90% of the time without it always being true that the key has been pressed a moment afterward, and I have a TimeManager class that keeps track of the individual frames where I could do a simple InputManager.Flush() every other frame if I knew some way of actually 'removing' the current state of the lastKeyboardState.

\$\endgroup\$
0
\$\begingroup\$

Actually you can simply do:

private static void Flush()
{
    lastKeyboardState = new KeyboardState();
}
\$\endgroup\$
0
\$\begingroup\$

I'm assuming the rest of your code goes somewhat like this:

        if(KeyPressed(Keys.Enter))
            bringUpMenu();

        if(KeyPressed(Keys.Enter))
            selectMenuOption();

and you want to flush the values somewhere in the middle? Well the solution I thought of would be to add the line "keyboardState = lastKeyboardState" to the first if statement...

        if(KeyPressed(Keys.Enter))
        {
            bringUpMenu();
            keyboardState = lastKeyboardState;
        }

        if(KeyPressed(Keys.Enter))
            selectMenuOption();

That way, it sort of "jams up" the keyboard until next frame. That way, the user will have to release and then press enter again to activate any menu selections.

One thing I'd advise against doing though is using this workaround too much - otherwise to the user it might almost seem as if their keyboard is swallowing all their keypresses whenever they select a tile / menu option - which is sort of what it looks like you want to do, but not always a good idea to do a lot.

Hope this helps!

\$\endgroup\$

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.