Handling ALL input without current/previous state checks?

Hey,

I’m new to MonoGame and I think I’m having a little difficulty understanding input. To me, it looks like I’m expected to get input by querying the current and previous states of the mouse/keyboard/gamepad/etc. This seems flawed unless I’m misunderstanding something.

What keeps input from being missed if the player/user presses and releases a button on the same iteration of the update loop? What if the order of button presses matters and two or more buttons are triggered in the same iteration of the update loop?

Shouldn’t input be a list or array the framework pushes to that the developer reads/pops from?

I assume the update method is usually called far more frequently than draw. If that’s the case, I’m sure for most purposes what I’m describing isn’t an issue, but if what I’m describing is true… it makes me hesitate to use a framework that I know would be dropping input given the right combination of performance problems when there’s no reason for the implementation to be this way (or for this to be the ONLY implementation on retrieving input).

Thanks for any help,
Mythics

If you want to record for replays, push/pop is a good idea, remember input is scanned many times per second [ depending on; usually, your refresh rate/update rate ]… stream inputs is one way to go…

I believe each platform has jitters regarding Input, so, which platform[s] are you aiming for, so these can be addressed respectively…

Also, Hi, and welcome to the forum…

1 Like

What I have right now is only a concept, but my intent was to target Windows. Assuming I make it far enough to do so, I’d like to eventually include Mac as well as Linux.

Regarding the user tapping a key/button rapidly, how do you (on Windows) avoid missing that input if it happens during the same update iteration?

If update is called and you check the key state and it is unpressed, but then the user taps the key and releases it before the next update method is called… there’s no input buffer to read from, from what I see in MonoGame.

I don’t know a lot about hardware, but I would expect the OS receives interrupts at a speed faster than how frequently the update method is going to be called… so isn’t there a bit too much room there for issues? Or am I missing something?

Just a quick search, maybe this helps?

Search phrase:

xna input controls

Search results:

https://www.bing.com/search?q=xna+input+controls&form=EDNTHT&mkt=en-gb&httpsmsn=1&refig=9cbaaa48cf4447b18ad87019a4a01958&sp=-1&ghc=1&pq=undefined&sc=0-8&qs=n&sk=&cvid=9cbaaa48cf4447b18ad87019a4a01958

1 Like

It’s true, there is a potential for missing keystrokes. In practice this rarely becomes a problem, because humans just aren’t fast enough to beat the update loop. At very low update rates you will be missing keystrokes though.

In my experience it starts to get dangerous once you get below 20 updates per second. A quick search on human keypress timings also confirms that number. The average keydown time seems to be around 100 ms, where 50 ms seems to be the lower end.

To give you an idea what those update rates mean for video games. The target framerate (at least on PC) is usually 60+ fps. 30 fps is usually considered the minimum. At 20 or less fps most people would probably consider a game as unplayable. Of course it also depends on the type of game.

While not being a big problem in practice, I think there is a bit of a weakness here. Having an event-based alternative to the polling system would be nice. MonoGame does have an event-based alternative for keyboard input, but that seems to be mostly geared towards text input.

Not really, the most common scenario is to have one update for every draw.

Yes, Windows uses an event-based input system under the hood.

Interestingly enough if you are a fast typer words that end in er and other letter combos, have the distinction that a user can often press the e before r but not release it until the r is already being pressed.
This means more often you are more concerned with a delaying mechanic to registering events at 1/60th of a second.
Their is a underlying array you can access though.

Yeah, that makes sense. If you care about the exact order of keypresses, you can run into problems even sooner.

Would that require digging into MonoGame code, or did I miss something from the public API?

1 Like

That’d be my exact question as well. I’m having some difficulty finding anything related to what I’m interested in getting in the public API outside of keyboard input specifically related to text input.

Also Markus, thank you for your above reply as well. You described my own thoughts and concerns quite accurately.

This technique is what I’ve use record all keys events on a single frame, tho I just use LIST ^ _^ y

//!_______________________________________________________________
//!
//! NAMESPACE CONTEXT
//!_______________________________________________________________

//!_______________________________________________________________
//!
//! NAMESPACE GROUP
//!_______________________________________________________________
//!
namespace ZGDK.IO
{

//!___________________________________________________________
//! 
//! KEYBOARD DEVICE CLASS
//!___________________________________________________________
//!     

/// <summary>
/// Keyboard table 
/// </summary>
public class Keyboard 
{


    //!___________________________________________________________
    //! 
    //! F I E L D S
    //!___________________________________________________________
    
    internal XFI.KeyboardState    __KeyBoardState;  
    //!
    internal bool                 __KeyEvent;
    internal bool                 __KeyDownEvent;
    internal bool                 __KeyUpEvent;
    internal bool                 __KeyHoldEvent;
    //!
    internal SCG.List<XFI.Keys>   __KeysPressed;
    internal SCG.List<XFI.Keys>   __KeysHeld;
    internal SCG.List<XFI.Keys>   __KeysReleased;
    //!
    internal XFI.Keys[]           __NewKeyPressed;
    internal XFI.Keys[]           __LastKeys;
    internal XFI.Keys             __LastSingleKey;
    //!
    internal bool                 __CtrlHold;
    internal bool                 __ShiftHold;
    internal bool                 __AltHold;
    //!
    internal bool                 __KeyboardInit;
    internal bool                 __Disposed;
    //!
    private int                   __ThisTime;
    private bool                  __Keyfound;
    private int                   __HeldCnt;


    //!___________________________________________________________
    //! 
    //! C O N S T R U C T O R
    //!___________________________________________________________
    
    /// <summary>
    /// 
    /// </summary>
    internal Keyboard()
    {
        __KeyboardInit = false;
        __Disposed     = false;

        ZGLOBAL._Keyboard = this;
    }


    //!___________________________________________________________
    //!
    //! D E S T R U C T O R
    //!___________________________________________________________

    /// <summary>
    /// Dispose by destructor
    /// </summary>
    ~Keyboard()
    {
        _M_CleanUp();
    }

    /// <summary>
    /// This table should not base on IDisposable, dispose this internally
    /// </summary>
    internal void _M_CleanUp() 
    {  
                  
        if ( !__Disposed )
        {
            if ( __KeysPressed   != null ) { __KeysPressed   = null; }
            if ( __KeysHeld      != null ) { __KeysHeld      = null; }
            if ( __KeysReleased  != null ) { __KeysReleased  = null; }
            if ( __NewKeyPressed != null ) { __NewKeyPressed = null; }
            if ( __LastKeys      != null ) { __LastKeys      = null; }

            ZGLOBAL._Keyboard = null;

            __KeyboardInit    = false;
            __Disposed        = true;
        }

    }


    //!___________________________________________________________
    //! 
    //! I N I T I A L I Z E
    //!___________________________________________________________
    
    internal void _M_Initialize()
    {
        __KeysPressed    = new SCG.List<XFI.Keys>();
        __KeysHeld       = new SCG.List<XFI.Keys>();        
        __KeysReleased   = new SCG.List<XFI.Keys>();     

        __NewKeyPressed  = new XFI.Keys[0];
        __LastKeys       = new XFI.Keys[0];            
        __LastKeys       = XFI.Keyboard.GetState().GetPressedKeys();
        __LastSingleKey  = XFI.Keys.None;

        __KeyboardInit   = true;
    }


    //!___________________________________________________________
    //! 
    //! U P D A T E
    //!___________________________________________________________
    
    internal void _M_Update()
    {

        //! If keyboard not yet initialize exit from here
        if (!__KeyboardInit || __Disposed ) return;
        
        //------------------------------------------------------//


        //! Local memvars needed
        __ThisTime     = UPS_FPS.__TICKS_NOW;
        __Keyfound     = false;

        //! Reset keyboard events
        __KeyEvent     = false;
        __KeyDownEvent = false;
        __KeyUpEvent   = false;
        __KeyHoldEvent = false;

        //! Reset special holding keys
        __AltHold      = false;
        __CtrlHold     = false;
        __ShiftHold    = false;


        //------------------------------------------------------//

        //! Clear previous frame key pressed and keys released
        if ( __KeysPressed.Count > 0 ) { __KeysPressed.Clear();  }
        if ( __KeysReleased.Count> 0 ) { __KeysReleased.Clear(); }


        //------------------------------------------------------//

        //! Control keys should work only games is the active app
        if ( ! ZGLOBAL._GameFramework.IsActive )
        {
            return;
        }


        //------------------------------------------------------//

        //! Saving new keybaord state for this frame
        __KeyBoardState = XFI.Keyboard.GetState();
        __NewKeyPressed = __KeyBoardState.GetPressedKeys();
        

        //------------------------------------------------------//

        //! Update special holding keys
        if( __KeyBoardState.IsKeyDown(XFI.Keys.LeftShift)   ||
            __KeyBoardState.IsKeyDown(XFI.Keys.RightShift)     )
        {
            __ShiftHold = true;
        }

        if( __KeyBoardState.IsKeyDown(XFI.Keys.LeftControl) ||
            __KeyBoardState.IsKeyDown(XFI.Keys.RightControl)   )
        {
            __CtrlHold = true;
        }

        if( __KeyBoardState.IsKeyDown(XFI.Keys.LeftAlt)     ||
            __KeyBoardState.IsKeyDown(XFI.Keys.RightAlt)       )
        {
            __AltHold = true;
        }


        //------------------------------------------------------//

        // Update newly pressed keys and held keys for this frame
        for ( int N = 0; N < __NewKeyPressed.Length; N++ )
        {

            //! Keyboard events occur | A key was pressed down or still holding some old keys
            __KeyEvent = true;                
            __Keyfound = false;

            // Find if new keys already been held
            for ( int H = 0; H < __KeysHeld.Count ; H++ )
            {
                if ( __NewKeyPressed[N] == __KeysHeld[H] )
                {
                    __Keyfound = true;
                    break;
                }
            }

            //! If not been held 
            if ( ! __Keyfound )
            {
                //! Add as newly pressed key
                __KeysPressed.Add( __NewKeyPressed[N] );

                //! Add also as being held
                __KeysHeld.Add( __NewKeyPressed[N] );

                //! A key down events
                __KeyDownEvent = true;
            }
            
        }


        //------------------------------------------------------//

        //! Remove all keys from being held if released
        __HeldCnt = __KeysHeld.Count - 1;
        //!
        while ( __HeldCnt >= 0 )
        {                
            if (  __KeyBoardState.IsKeyUp( __KeysHeld[ __HeldCnt ] ) )
            {
                //! Add it to released keys for this frame
                __KeysReleased.Add( __KeysHeld[ __HeldCnt ] );

                //! Remove from keys from being held
                __KeysHeld.RemoveAt( __HeldCnt );

                //! Keyboard events occur | a keys was released
                __KeyEvent   = true;
                __KeyUpEvent = true;
            }
            __HeldCnt--;
        }
        //! 
        if( __KeysHeld.Count>0 ) __KeyHoldEvent = true;


        //------------------------------------------------------//

        //! Update last keys and last single keys
        __LastKeys = __NewKeyPressed;
        //!
        if( __NewKeyPressed.Length >0 )
        {
            //! Get the last single key being pressed down
            __LastSingleKey = __NewKeyPressed[ __NewKeyPressed.Length-1 ];
        }
        else
        {
            __LastSingleKey = XFI.Keys.None;
        }
       
    }


    //!___________________________________________________________
    //! 
    //! P U B L I C   M E T H O D S
    //!___________________________________________________________

    /// <summary>
    /// Inquire if any keyboard events currently occurring from this frame
    /// wheather a key down, key up or key holding events.
    /// </summary>
    public bool IsHasEvents()
    {
        return __KeyEvent;
    }

    /// <summary>
    /// Inquire if key down event occur from this frame.
    /// </summary>
    public bool IsEventsKeyDown()
    {
        return __KeyDownEvent;
    }

    /// <summary>
    /// Inquire if key up event occur from this frame.
    /// </summary>
    public bool IsEventsKeyUp()
    {
        return __KeyUpEvent;
    }

    /// <summary>
    /// Inquire if keys holding still occurring from this frame.
    /// </summary>
    /// <returns></returns>
    public bool IsEventsKeyHold()
    {
        return __KeyHoldEvent;
    }


    /// <summary>
    /// Get all last keys currently being pressed down or being held from this frame
    /// </summary>
    /// <returns>Returns Keys</returns>
    public XFI.Keys[] GetLastKeys()
    {
        return __LastKeys;
    }  
          
    /// <summary>
    /// Get the last single key currently pressed down or being held from this frame
    /// </summary>
    /// <returns>Returns Key, otherwise return Keys.None if there is no keyboard event</returns>
    public XFI.Keys GetLastKey()
    {
        return __LastSingleKey;
    }


    /// <summary>
    /// Inquire if left or right CTRL key is currently on hold
    /// </summary>
    public bool IsCtrlHold()
    {
        return __CtrlHold;
    }

    /// <summary>
    /// Inquire if left or right SHIFT key is currently on hold
    /// </summary>
    public bool IsShiftHold()
    {
        return __ShiftHold;
    }

    /// <summary>
    /// Inquire if left or right ALT key is currently on hold
    /// </summary>
    public bool IsAltHold()
    {
        return __AltHold;
    }

    /// <summary>
    /// Inquire if Key has been pressed, keys on hold not accounted
    /// </summary>
    /// <param name="pKey">Key</param>
    /// <returns>Returns true if key was pressed, otherwise false</returns>
    public bool IsKeyPressed(XFI.Keys pKey)
    {
        if (__Disposed) return false;

        if (__KeysPressed.Contains(pKey))
        {
            return true;
        }

        return false;         
    }

    /// <summary>
    /// Inquire if Key is currently on hold
    /// </summary>
    /// <param name="pKey">Key</param>
    /// <returns>Return true if on hold, otherwise false</returns>
    public bool IsKeyHoldDown(XFI.Keys pKey)
    {
        if (__Disposed) return false;

        for (int i = 0; i < __KeysHeld.Count; i++)
        {
            if (__KeysHeld[i] == pKey) return true;
        }

        return false;
    }

    /// <summary>
    /// Inquire if key was released from this frame
    /// </summary>
    /// <param name="pKey">Key</param>
    /// <returns>Returns true if key was released, otherwise returns false</returns>
    public bool IsKeyReleased(XFI.Keys pKey)
    {
        if (__Disposed) return false;

        for (int i = 0; i < __KeysReleased.Count; i++)
        {
            if (__KeysReleased[i] == pKey) return true;
        }

        return false;
    }


}//EndClass Keyboard

} //EndNamspace

1 Like

Ty for sharing your code (very nice of you), but all I really see is you’re comparing key states from frame to frame and adding changes to a list. You even have a comment saying as much here:

     //! Saving new keybaord state for this frame
    __KeyBoardState = XFI.Keyboard.GetState();

That is where you poll for the current state and then your next bit of code compares to the previous state. If the user presses a key after you call XFI.Keyboard.GetState() and releases it before you call XFI.Keyboard.GetState() again, your code will see the key up in one frame and up again in the next and therefore won’t catch the input.

2 Likes

Hello Mythics,

It’s was my Javascript/TypeScript keyboard handling class I ported to Monogame, so far I have no problem using it and may answer your original inquiry “Handling ALL input without current/previous state checks?”

If I want to know if Key.UP button was released I just issue the code below, without needing to know whether if it was been presssed down the first time or if it still on hold.

if( myKeyboard.IsKeyRelease( XFI.Key.Up ) ) { }

// EDIT : This you can also inquire multiple keys just ask and forget :smiley:

if( myKeyboard.IsKeyHoldDown( XFI.Keys.Up ) && myKeyboard.IsKeyHoldDown( XFI.Keys.Left )
{
// Move left upwards diagonally
}
else if( myKeyboard.IsKeyHoldDown( XFI.Keys.Down ) && myKeyboard.IsKeyHoldDown( XFI.Keys.Right )
{
// Move right downwards diagonally
}

Cheers ^_^y

I was referring to getkeyspressed() it returns a array of keys[] pressed when called.

I don’t know that this question was really ever resolved. I’ve had this annoyance with MonoGame for a long time. Sometimes keystrokes just don’t appear. Sometimes a lot, sometimes rarely. My project is a psudo-terminal using MonoGame for rendering. I can notice it while typing into my app. I suspect it must be just typing too fast for the 60fps system.

It’s weird and annoying. Does anyone know of any other cross-platform .NET library we could use for better keyboard handling?

If you want to write text, you should use the Window.TextInput event. For example:

protected override void Initialize() {
    Window.TextInput += ProcessTextInput;

    base.Initialize();
}

private void ProcessTextInput(object sender, TextInputEventArgs e) {
    // Do something with the character in 'e'.
}
1 Like