
The idea of this tutorial is to help understand how to create flexible classes for user input that can be used in any game that you create. As we all know DirectX isn’t the most user friendly API to use hence why I am writing this! This is the first tutorial that I have written, if you have any questions drop us an email.
1. The main input system class
To control your input system you need a main input system class. This class will control all the input devices attached to your system. Create a CInputSystem.h and a CInputSystem.cpp file and set up the default layout as appropriate. Ensure that you link the project to the DirectX libraries dxguid.lib and dinput8.lib, also make sure you include the dinput.h header. This can be done easily by using the following code:
// LINKS TO THE DIRECT X LIBRARY //
#pragma comment( lib, "Dinput8.lib" )
The next step is to create an appropriate constructor. All devices attached to the input system need to know about the HWND object, so the device priorities can be set (e.g. you only want the controller to work when the game is in the foreground). The input system itself needs to know about the HINSTANCE object, hence the constructor needs to accept these two values.
CInputSystem(HWND
hWnd, HINSTANCE appInstance) ;
// Constructor
~CInputSystem() ;
// Destructor
The main member needed in this input system class at present is the DirectX8 Input device which will be instantiated within the CInputSystem constructor. Create this member in the header as:
LPDIRECTINPUT8 deviceDirectInput ; // The input device
Next, we need to create three functions that allow us to easily acquire, unacquire and update the attached devices. These functions can all be of type void as no return is necessary.
void
AcquireAll();
// Acquires all the devices
Once these have all been declare in the header, it is time to create the default bodies in the cpp file. When these bodies have been created, you first need to write the constructor code.
CInputSystem::CInputSystem(HWND
hWnd, HINSTANCE appInstance)
DirectInput8Create( appInstance,
DIRECTINPUT_VERSION,
IID_IDirectInput8,(void
**)&deviceDirectInput,
NULL );
// Create the dinput object
this->AcquireAll();
// Acquire
all the devices
// DESTRUCTOR //
CInputSystem::~CInputSystem()
2. The Device Structure

With this design in mind it is now time to create the CInputDevice abstract class.
This class is
the base class of all devices attached to the system. It will contain the actual device pointer
(LPDIRECTINPUTDEVICE8), the device name, the button/key states and the number
of buttons/keys. Functions include one that allows the device to be acquired
and another that updates this particular device (which is a pure virtual
function to make the class abstract, also this is a different function between
different devices). A default constructor and destructor is used.
LPDIRECTINPUTDEVICE8 device ;
// The
controller device
int
numberOfButtons ;
// Number of buttons/keys
void
Acquire () ;
// Acquire
CInputDevice() ;
//
Constructor
The body of the
constructor and destructor is pretty straight forward, note here that the
buttons and device pointers are just set to null, this is because the device
that extends this class (e.g. CKeyboard) will initialise the pointers
appropriately in their constructor.
// CONSTRUCTOR //
// DESTRUCTOR //
// ACQUIRE //
The ResetButtons function simply sets all the values in the buttons array to 0, which means that all buttons are currently depressed.
void CInputDevice::ResetButtons()
That’s really it
for the base class, again this will change as you add more functionality,
however for this tutorial only one more function will be added at this time,
Namely the Event(int button) function. This is designed to return the value of
the button sent in (e.g. if we call Event(‘DIK_P’) we are really checking to see if key p is
currently pressed or not! ). The function prototype needs to be added to the
header and then a body needs to be created, similar to the one below:
char CInputDevice::Event(int button)
4. CKeyboard class
The following class is the keyboard class, which is obviously used to check for key presses. The main object of this class is to set up the device as a keyboard object, create the array for button/key state checking, and then provide functionality for updating the device each frame.
First ensure
that you make this class extend the CInputDevice class. Next we set up the
constructor, which is used to set up the appropriate device, in this case the
keyboard.
//
CONSTRUCTOR //
// Set the data
format to a keyboard, and level to foreground
device->SetCooperativeLevel(hWnd,
DISCL_FOREGROUND | DISCL_NONEXCLUSIVE) ;
// finally
acquire the device
// Set up the
button/key state array
All should be self explanitary, the SetCooperativeLevel function allows you to set priority of the keyboard for this application or game, for example when your application loses priority (is no longer in the foreground) the device will be unacquired.
The SetDataFormat function basically allows you to specify how the data is obtained from the device, typical formats are:
Keyboard = c_dfDIKeyboard
Mouse = c_dfDIMouse or
c_dfDIMouse2
Joystick = c_dfDIJoystick or
c_dfDIJoystick2
The final part
of this class is the update function:
if
(FAILED(device->GetDeviceState(numberOfButtons, (LPVOID)buttons)))
This basically sets the button/key state array to the appropriate values, 1 if the key is down and 0 if the key is up. More functionality can be added, however this is only a very basic look into direct input.
5. Finishing off…
Before you can
use the input system class you now need to add an event function. This will be
called outside of the input system class itself in order to find out if a
particular event has occurred on a particular controller. For this example the
singleDevice pointer is a pointer of type CKeyboard which has been set up in
the input system constructor. This function will check the keyboard for an
button/key press that has been passed in.
char CInputSystem::Event(int button)
The idea now is to create other direct input concrete devices that can be used such as a joystick and a mouse. The devices can then be held in a list inside the input system class, and can be polled for state changes every frame.
To use the input system, simply create the input system object from within your engine and then whenever you wish to check the input update the input system and then check for any events. For example:
void CEngine::CheckInput()
I will eventually create a controller class, a mouse class and create a more flexible input system. I hope you could follow this if not there must be bits that are useful to you!!
© Stephen Brown
2003