Experiments In Game Programming
Main
 Home
XNA - C#
 Coming Soon!
 Level Editing - GTK
DirectX 9 - C++
 Downloads
 Disclaimer
 Introduction
Part 1 - DirectX
 1 - Breakout
 2 - Create DX
 3 - 2d Images
 4 - 3d Models
 5 - Cameras & Lights
 6 - Animation Timing
 7 - Keyboard/Mouse
 8 - Sound
Part 2 - Breakout
 1 - Art and Sounds
 2 - The Menu
 3 - Starting Breakout
 4 - The Level
 5 - The Paddle
 6 - The Ball
 7 - Finishing Touches

Untitled Document

Chapter 7

 

Keyboard and Mouse Input

 

 

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.

bool InputManager::init(HINSTANCE hInst, HWND wndHandle)
{
     HRESULT hr;

     // Create a direct input object

     hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION,
     IID_IDirectInput8, (void**)&dInput, NULL);

     if FAILED(hr){
         return FALSE;
     }

     // 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.

Changes to GameMain's Init:
bool GameMain::init(HWND wndHandle, HINSTANCE hInst)
{
     ...

     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.

Direct Input Keys:
DIK_A DIK_1 DIK_NUMPAD5 DIK_NUMLOCK
DIK_B DIK_2 DIK_NUMPAD6 DIK_SCROLL
DIK_C DIK_3 DIK_NUMPAD7 DIK_SUBTRACT
DIK_D DIK_4 DIK_NUMPAD8 DIK_ADD
DIK_E DIK_5 DIK_NUMPAD9 DIK_DECIMAL
DIK_F DIK_6 DIK_ESCAPE DIK_NUMPADENTER
DIK_F DIK_7 DIK_MINUS DIK_RCONTROL
DIK_G DIK_8 DIK_EQUALS DIK_DIVIDE
DIK_H DIK_9 DIK_BACK DIK_NUMPADCOMMA
DIK_I DIK_0 DIK_TAB DIK_RMENU
DIK_J DIK_F1 DIK_LBRACKET DIK_SYSRQ
DIK_K DIK_F2 DIK_RBRACKET DIK_HOME
DIK_L DIK_F3 DIK_RETURN DIK_UP
DIK_M DIK_F4 DIK_LCONTROL DIK_PRIOR
DIK_N DIK_F5 DIK_SEMICOLON DIK_LEFT
DIK_O DIK_F6 DIK_APOSTROPHE DIK_RIGHT
DIK_P DIK_F7 DIK_GRAVE DIK_END
DIK_Q DIK_F8 DIK_LSHIFT DIK_DOWN
DIK_R DIK_F9 DIK_BACKSLASH DIK_NEXT
DIK_S DIK_F10 DIK_COMMA DIK_INSERT
DIK_T DIK_F11 DIK_PERIOD DIK_DELETE
DIK_U DIK_F12 DIK_SLASH DIK_LWIN
DIK_V DIK_NUMPAD0 DIK_RSHIFT DIK_RWIN
DIK_W DIK_NUMPAD1 DIK_MULTIPLY DIK_APPS
DIK_X DIK_NUMPAD2 DIK_LMENU DIK_PAUSE
DIK_Y DIK_NUMPAD3 DIK_SPACE  
DIK_Z DIK_NUMPAD4 DIK_CAPITAL  



 ©2008 David Whittaker