Welcome To The Intro
Before you take on your next blockbuster hit which will sport an
unprecedented 3D Engine with real-time ray tracing and original music
scores along with those realistic sounds of the ocean, do not forget
one important part of the final product, user input. The use of
DirectInput can easily improve the wealth of your game with its tons of
features and input devices it supports. Aside from the advanced
techniques of DirectInput, even the simpler methods of use can out do
your basic form events. So right now, I'm hoping to teach you, the
reader, the ins and outs of DirectInput and handling the keyboard so
you can “Get Input” with your keyboard.
Get The Setup
Before we can officially start using DirectInput, you must make sure
you have it referenced in your .NET solution and then actually
"Imported" into the code file where our DirectInput Keyboard class will
be created.
I went ahead and created a project for myself named
"InputKeyboard" of the Windows Application type but you can name it
whatever your heart desires. For the actual source files, there are
two, one for the test form and another for the class we will write to
handle the keyboard device. The form file name is “InputTutorial.vb”
and the class file is “InputKeyboard.vb”. Now, you should be able to
see the Solutions Explorer on the right side of the GUI or the tab for
it, if you don't see it, and then click the tab for it. If you see no
tab, then click on the View in the menu, then Solution Explorer.
Once
you have that window shown, you'll see collapsed tree by the name
"References", right-click on that and go to add references. It'll bring
you to a dialog showing a bunch of DLLs you can reference to your
project but scroll down the list "Microsoft.DirectX" along with
"Microsoft.DirectX.DirectInput" and double-click on each and press OK
on the dialog box. If you don't see it, you may have to look into
installing DirectX9 on to your computer (www.microsoft.com/directx).
And if all goes well, we're almost ready to begin the coding!
Now
that we have our project and references setup, we can simply add a
couple lines to the top of the "InputKeyboard.vb" source file.
- Imports Microsoft.DirectX
- Imports Microsoft.DirectX.DirectInput
These two lines import the reference of the two
namespaces, DirectX and DirectInput, into our source files allowing for
an easier access to its content. If we didn't import it to the source
file, we would have to type Microsoft.DirectX. etc... for every single
thing...too much typing.
Where's My Keyboard
The first thing you'll need in order to retrieve input from the
keyboard device, is a newly created keyboard device(obviously).
Actually creating the Device is a simple as it can be. After the device
is created you will need to set the type of data that will be read by
the device and what priority and/or cooperative level it will have
along with the rest of the applications also using that device type (ie
Keyboard or Mouse).
- MyDevice = New Device(SystemGuid.Keyboard) 'Create the device, simple :)
- MyDevice.SetDataFormat(DeviceDataFormat.Keyboard) 'Our Device Should Read "Keyboard" Data
- MyDevice.SetCooperativeLevel(Owner, Flags) 'Owner is the form which owns the Device
- MyDevice.Properties.BufferSize = 8 'size of buffer, how much buffered data will be kept, more later
- MyDevice.Acquire()
We create the device as you would create a new instance
of any class, but you must specify the type Device you’re creating with
the reference to the GUID, SystemGuid.Keyboard is our reference to the
keyboard, and should generally work with a majority of computers. The
next line sets the data format to Keyboard which is exactly what we
want.
The third line sets up the cooperative level for the
device, which in human terms is how will the device be handling data
from the keyboard in response to other applications. Remember, other
applications use the keyboard to, so we need to know how we will act
when the form using our Keyboard is not in focus and what not.
The
owner variable is the reference to the form handling the device, and
the variable flag is an enumerated type of CooperativeLevelFlags held
within the DirectInput namespace.
- Enum CooperativeLevelFlags
- Background 'The device window can be the focus or not the focused window and input will still be retrieved.
- Exclusive 'The application gains right to exclusive access tothe keyboard, or being the only application to use the keyboard deviceat the time.
- Foreground 'The device window must be focused for the device toretrieve data, if not, the device will be lost and you must reacquireit before being able to use it again.
- NonExclusive 'Your application will allow sharing of the keyboard device,(but some other apps might not :P)
- NoWindowsKey 'Disables use of the windows key(so that start menu won't popup when you press it
- End Enum
The combination of the flags should be
Background or Foreground and Exclusive or NonExclusive, but be in mind
some combinations will not work with the keyboard and mouse like
Background and Exclusive because it forms a weird cooperative level,
though the SDK does say it is possible for some types of devices.
The
second to last line of code assigned the value of 8 to the property
buffer size. It generally tells you that you can save up to 8 inputs
from the keyboard at a time to read from the keyboard data buffer. And
finally, the last code calls the Acquire procedure and that generally
means we are ready to get some input!
I'd Like Some Data
Now that we know how the wonderful keyboard works and how it’s created,
we will learn how to use it! From my knowledge (and I am no
professional I might add), there are 2 basic ways of retrieving data
from the Keyboard device in DirectInput. It is either "Immediate" or
"Buffered". Both methods can be useful for a project so creating
support for both integrated could be quite helpful.
With
Immediate Data, DirectInput takes a quick snapshot of the current
status of the Keyboard and returns the status of all of the available
keys. This can be useful when you need quick input on something that
doesn't require of knowing if the key was press down, or let go. I use
these method for debugging purposes a lot too, placing the simple input
code somewhere far out from my actual input code for the game itself.
- Dim State As KeyboardState
- State = m_Device.GetCurrentKeyboardState()
The above code retrieves the snapshot of the device keys
into the class KeyboardState which is also an array. You can get a
retrieve the input of the key by taking what key you want from the
DirectInput.Key enumeration as the index in the KeyboardState array.
The values are simply just Booleans, true and false for up or down.
When
receiving input with buffered data, the device had recorded the last
inputs into the keyboard up to the size of the Buffer (set when we
created the device). This input proves far superior so you can really
tell how many times a key was pressed. With immediate, between the
times of calling for the input, that same key may have been pressed.
- '//Local Variables
- Dim Data As BufferedData, Buffer As BufferedDataCollection, continue As Boolean
- '//Get Input Data
- Try
- Buffer = m_Device.GetBufferedData()
- Catch e As InputException 'Handles our errors, will try to recapture the device
- Do
- continue = False
- Application.DoEvents()
- Try
- m_Device.Acquire()
- Catch e2 As InputLostException
- continue = True
- Catch e3 As OtherApplicationHasPriorityException
- continue = True
- End Try
- If Not Owner.Created Then Exit Do
- Loop While continue
- End Try
- '//Process Buffered Data
- If Buffer Is Nothing Then Exit Sub
- 'Go through each element of data, and see if the key
- 'was pressed or let go
- For Each Data In Buffer
- If Data.Data And &H80 Then
- RaiseEvent KeyDown(Data.Offset) 'Calls KeyDown Event
- Else
- RaiseEvent KeyUp(Data.Offset) 'Calls KeyUp Event
- End If
- Next
Now you're probably thinking, what the heck happened? The immediate
data was so easy to get, while the buffered has like 20x more code.
Let’s decipher this baby! The Data (BufferedData) represents one
element in the collection of BufferedData, or the Buffer variable. So
BufferedData is contained within BufferedDataCollection. The continue
Boolean is used for handling any errors we have with getting input from
the device.
We use to try statement to make sure if the call to
retrieve the buffered data fails or not, if it does fail then we have a
list of catch statements to help us from returned errors and telling us
to keep continuing. Now from this code you can see the Owner.Created
statement and that'll help us from keeping in an infinite loop. If the
form isn't even visible or created yet, then we will quit as we will
never get the device acquired until we do.
After the error
handling we check if there is any buffered data, if there isn't then we
exit the sub routine, if there is we process it. We process the whole
collection by making a for...each loop to go through each element
contained. We check the bytes using the Data property of the
BufferedData class and "anding" it to the hex value of &H80. This
allows us to retrieve whether the button was let go or let down. When
we figure out which it was (up or down), we can call our own custom
events (similar to Form Events) to handle the input.
We now have the basis of creating our own Keyboard class!
I'm A Class Clown
Now that we have the essentials and basic usage of the Keyboard device,
we can create a class to wrap it up and make using it simpler, robust,
and reusable in any project! The design for the class is very simple
and it is downloadable and inside "InputKeyboard.vb" file.
It
follows what I just created earlier in the tutorial pretty much
identically. The constructor for the class will initialize the Device
and set it up for use. I created a special sub routine to manually set
the buffer size and there are two other functions in which you can
receive keyboard data. The GetKeyState function retrieves immediate
data of a particular key you want input data for, while the Update sub
reads all the buffered Data and processes it by calling custom events.
Our
events are simply KeyUp, and KeyDown. If you're good, you could think
of a way to manage if a key is still being pressed. But to write the
actual events to the class events you do it the same way you would for
any events in VB .NET, but if you don't know, here's some extra info.
- Private WithEvents Keyboard As InputKeyboard
- Private Sub Keyboard_KeyUp(TheKey As Key) Handles InputKeyboard.KeyUp
- If TheKey = Key.Escape Then DoFoo
- End Sub
You can write the sub with any name you want it to be,
but the arguments much match up with the arguments of the Event. Every
time the event is raised, this sub will also be called to handle the
event. You can also create multiple subs and functions that can handle
the same event being rose which is pretty cool. Be sure that you
include the WithEvents keyword with the declaration of the class. It
tells the compiler that we're enabling be able to write the events for
that class.
I'm Tired And Going To Sleep
I'm finally concluding this epic tutorial by spending the next hour
playing the cheap game known as Madden 2004. I've been writing this
tutorial for the last 90 minutes and I'm glad I'm finally done. I hope
this can help you understand the basics of DirectInput using the
Keyboard. I'm not the best at this; I'm just sharing my knowledge on
the subject which I learned straight from the SDK and putting it into
human words.
If you have any questions or comments you can email
me at EnglishM1@aol.com, IM at X WookieMan X, or see me rounding the
forums at Lucky's by the handle of Luminous. Well I'm out, later
Gregory English
Example Program