Every game requires input from the user, DirectX provides for this with Direct
Input. In this chapter we will setup an InputManager class to monitor input
from the mouse and keyboard using Direct Input.
Topics Covered in this Chapter:
Creating An InputManager Class
Getting Mouse and Keyboard Input from our InputManager
To gather input from the keyboard and mouse using direct Input we first need
to create a mouse device, and a keyboard device. The keyboard device lets us
check if a key is pressed, and the mouse device lets us check if the mouse is
moving and how fast.
Take a look at the InputManager class in the chapter 7 code. The InputManager
starts the same as all of our other classes, and the first function of interest
is init. Init creates
our mouse and keyboard devices. The function first creates a direct input object,
once we have a direct input object we can create our mouse and keyboard devices.
Once the mouse and keyboard devices are created the data format and cooperation
level need to be set. Then the devices need to be acquired. Once the devices
are acquired they are ready for use.
// Create a device for monitoring the mouse
if FAILED(dInput->CreateDevice(GUID_SysMouse, &mouseDevice, NULL))
return FALSE;
if FAILED(mouseDevice->SetDataFormat(&c_dfDIMouse))
return FALSE;
if FAILED(mouseDevice->SetCooperativeLevel(wndHandle, DISCL_FOREGROUND
| DISCL_EXCLUSIVE))
return FALSE;
if FAILED(mouseDevice->Acquire())
return FALSE;
// Create a device for monitoring the keyboard
if (FAILED(dInput->CreateDevice(GUID_SysKeyboard, &keyboardDevice,
NULL)))
return false;
if (FAILED(keyboardDevice->SetDataFormat(&c_dfDIKeyboard)))
return false;
if (FAILED(keyboardDevice->SetCooperativeLevel(wndHandle, DISCL_BACKGROUND
| DISCL_NONEXCLUSIVE)))
return false;
if (FAILED(keyboardDevice->Acquire()))
return false;
return true;
}
Now that we have our devices, we need to ask them what their status is. The
getInput function calls GetDeviceState on
both devices, this gets the state of both the keyboard and mouse. If the GetDeviceState
call fails the device may have been lost, in this case a call to Acquire
is made to try and reacquire access to the device. The getInput
function needs to be called each time we want to get input from the keyboard
or mouse, it records the state of the device at the time it is called. That
recorded state is then checked with our get key functions and get mouse functions
that follow.
void InputManager::getInput()
{
HRESULT hr;
hr = mouseDevice->GetDeviceState(sizeof(DIMOUSESTATE),(LPVOID)&mouseState);
if (FAILED (hr))
{
// try and reacquire the input device
mouseDevice->Acquire();
}
keyboardDevice->GetDeviceState(sizeof(UCHAR[256]), (LPVOID)keyState);
if (FAILED (hr))
{
// try and reacquire the input device
keyboardDevice->Acquire();
}
}
The next 2 functions are getMouseMovingX and getMouseMovingY;
these 2 functions return how fast the mouse is moving in the X or Y direction
as an integer value. For the X a positive value is right, and a negative value
is left. For the Y a positive value is down, and a negative value is up.
int InputManager::getMouseMovingX()
{
return mouseState.lX;
}
int InputManager::getMouseMovingY()
{
return mouseState.lY;
}
The next function is isButtonDown, isButtonDown
returns just what its name indicates, whether or not the requested mouse button
is down or not.
bool InputManager::isButtonDown(int button)
{
//check the state of the button
if (mouseState.rgbButtons[button] & 0x80){
return true;
} else {
return false;
}
}
The next 2 functions, keyDown, and keyUp
return the state of the requested key, if it is down or up respectively. Both
require a direct input key to request, ie DIK_UP is the up arrow key. Included
at the end of this chapter is a table with many of the possible direct input
keys.
bool InputManager::keyDown(DWORD key)
{
//check the state of the key
if (keyState[key] & 0x80){
return true;
} else {
return false;
}
}
bool InputManager::keyUp(DWORD key)
{
//check the state of the key
if (keyState[key] & 0x80){
return false;
} else {
return true;
}
}
The next and last function is keyPressed,
keyPressed returns if the requested key has been
pressed and then released. It does this by recording the keyDown state the first
time it is called, if the key is recorded as down the next call checks if the
key has been released by calling keyUp. If the key has been both down and up,
keypressed returns true.
bool InputManager::keyPress(DWORD key)
{
//check for keydown
if (keyDown(key)){
keyPressState[key] = 1;
}
//check for key reaching the keydown state
if (keyPressState[key] == 1){
//check for key release
if (keyUp(key))
keyPressState[key] = 2;
}
//check if key has been pressed and released
if (keyPressState[key] == 2){
//reset the key status
keyPressState[key] = 0;
return true;
}
return false;
}
That completes the InputManager class. In order to test the InputManager I
have added it the GameMain, and using the TextManager from chapter 2 printout
the mouse status. To test the keyboard I have added checks for the arrow keys.
When the arrow keys are pressed, the position of the 2dSurface test is adjusted,
moving the image around the screen. An important note is the InputManager required
the instance handle for the application. GameMain from the previous chapters
did not have access to that. In order to init the InputManager I have added
hinstance to the GameMain init function call and pass the instance in from the
winmain.
input = new InputManager();
input->init(hInst, wndHandle);
//create a new frame timer
timer = new FrameTimer();
timer->init(60);
return true;
}
Changes to GameMain's Update:
void GameMain::update(void)
{
int framesToUpdate;
//call our update function
framesToUpdate = timer->framesToUpdate();
animTest->updateFrames(framesToUpdate);
//update the input state
input->getInput();
char textOut[60] = "";
sprintf(textOut,"X, and Y Mouse Movement = , x %02d, y %02d ", input->getMouseMovingX(),
input->getMouseMovingY());
int x = 60;
int y = 80;
if (input->keyDown(DIK_LEFT)){
x = 60;
testSurface->setPosition(x,y);
}
if (input->keyDown(DIK_RIGHT)){
x = 400;
testSurface->setPosition(x,y);
}
if (input->keyDown(DIK_UP)){
y = 80;
testSurface->setPosition(x,y);
}
if (input->keyDown(DIK_DOWN)){
y = 400;
testSurface->setPosition(x,y);
}
// begin rendering
dxManager->beginRender();
//game render calls go here
textManager->drawText(textOut, 30, 30, 300, 300);
testSurface->render(dxManager->getD3DDevice());
animTest->render(dxManager->getD3DDevice());
//end render
dxManager->endRender();
}
Compile and run the project to test the InputManager, the speed of the mouse
should be displayed at the top of the screen, and pressing the arrow keys should
move the test image to different corners of the screen.
Summing Up - Chapter 7
In this chapter we created an InputManager class that handles keyboard and
mouse input. We used direct input 8, direct input has not been updated since
directx 8. Below is a listing of many of the key definitions for use with direct
input calls.