DirectX:DirectInput:Tutorials:VBNET:DX9:Keyboard Handling Immediate

From GPWiki

Files:GUITutorial_warn.gif The Game Programming Wiki has moved! Files:GUITutorial_warn.gif

The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server.

However, the GPWiki forums are still active! Come say hello.

This tutorial covers the basics of accessing the Keyboard using managed DirectX in VB.Net.

DirectInput (The portion of DirectX we'll be dealing with) handles the keyboard in 2 ways: buffered and Immediate. Buffered means that DirectX will store all the sequential keycodes for you and is real handy for processes that involve actual typing. Immediate means that you'll just get the instant state of the keyboard returned the moment you poll it. This is real cool if you're making a game that requires split second reflexes, but less handy if you want to enter your name in the highscore list, unless of course, you happen to be called rrrrrrrrryyyaaaannnn.

Because I'm a very lazy person, I'm only going to cover the unbuffered part, if you feel that this is cheating, then I invite you to write your own buffered variant and press the nice edit button on top of this page. It's a wikki after all.

Now on to the code!

Public Class KeyboardEngine
    Private Const COOPLEVELKB As Microsoft.DirectX.DirectInput.CooperativeLevelFlags = Microsoft.DirectX.DirectInput.CooperativeLevelFlags.Foreground _
Or Microsoft.DirectX.DirectInput.CooperativeLevelFlags.Exclusive
    Private Dev As Microsoft.DirectX.DirectInput.Device = Nothing
    Private KeyboardAquired As Boolean = False
    Private KeyBoardState As Microsoft.DirectX.DirectInput.KeyboardState = Nothing

Well, this is our class, cunningly named "KeyboardEngine", it contains some variables that we want to store for internal use. We'll store the Directinput device here.(Dev) We'll also store the state of the keyboard(KeyboardAquired) here. This way we know if we actually have access to the keyboard, or if some other application stole it from us. I've also put the COOPLEVELKB constant here, this will tell windows on how we want use the device.


our options for that are:

  • NoWindowsKey = As long as your application has focus, the windows key doesn't work.
  • Background = A device with a background cooperative level can be acquired and used by an application at any time.
  • Foreground = Foreground cooperative level means that the application has access to data only when in the foreground.
  • NonExclusive = Other Applications can aquire the device in exclusive mode.
  • Exclusive = No other applications can aquire the device in exclusive mode.


I've set it to ForeGround and Exclusive, A combination that you'll probably want to use if you're writing your own game. Note, btw, that not all combinations are compatible, for instance, Background & exclusive cannot be set. And obviously, Foreground & Background cannot be set together either.

Finally, We'll store the state of the keyboard here( KeyBoardState) This variable is set every time we poll, and our Keypress function will use this to check on the state of our keys

That takes care of storing everything, now let's go on the actual programming. Our first task is to get the keyboard from DirectX, what we need for this is a window. Don't ask me why, this is just the way Windows works. It probably has to do with the fact that it's called Windows.

Public Sub New(ByVal ContainerWindow As Windows.Forms.Control)
        Try
            Dev = New Microsoft.DirectX.DirectInput.Device(Microsoft.DirectX.DirectInput.SystemGuid.Keyboard)
            Dev.SetCooperativeLevel(ContainerWindow, COOPLEVELKB)
            Dev.Acquire()
            KeyboardAquired = True
        Catch
            Console.Write("Could not acquire keyboard:")
            Console.Write(e.Message)
            If Not (Dev Is Nothing) Then Dev.Dispose()
            Dev = Nothing
            KeyboardAquired = False
        End Try
    End Function

The constructor tries to aquire the keyboard, if for some reason, it can't aquire the keyboard, it'll return an error message in the console and dispose of the DirectInput device.


Public Function Poll() As Boolean
        'Return true if device is still aquired, return false if it isn't
        Try
            If Not KeyboardAquired Then
                'We don't have the keyboard, try to get it back.
                Dev.Acquire()
                KeyboardAquired = True
            End If
 
            KeyBoardState = Dev.GetCurrentKeyboardState
 
            Return True
        Catch
            'Ack! probably lost the Device, return panicked false and set phasers to re-aquire.
            KeyboardAquired = False
            Return False
        End Try
    End Function

Our polling function, this function get's the current state and stores it in our internal KeyboardState. It should be called in once every gameloop. Actually, it should be called every time you're planning on doing any actual processing, There is no need to poll the keyboard if you're just rendering that loop. It returns True if everything was fine and dandy, it returns False if somehow the keyboard device was lost, a good indication that your game is not in the foreground anymore. It'll also try to automatically reaquire the keyboard if at all possible.

Public Function KeyPress(ByVal Key As Microsoft.DirectX.DirectInput.Key) As Boolean
        If (Not (KeyBoardState Is Nothing)) AndAlso KeyBoardState.Item(Key) Then
            Return True
        Else
            Return False
        End If
 
    End Function

Well, this is the second most important part of our function. After polling, Use this function to read out the state of the various keys. It'll see if Key in KeyBoardState has been set, indicating that it is currently in the down position.


Protected Overrides Sub Finalize()
        If Not (Dev Is Nothing) Then Dev.Dispose()
        Dev = Nothing
        KeyBoardState = Nothing
        KeyboardAquired = False
    End Sub
End Class

Our cleanup Function, this function makes sure that all the objects have been properly disposed off, you probably want to call this in the Finalize event.

And there you have it, A very simple Keyboard Catcher. This example could be extended to include keypress events, the DirectInput Buffered mode and a whole host of other nifty features. If, however, you just want some basic Keyboard functionality, this is a good basis.