Now that we have setup the mouse and keyboard, we need to get data from them. There are two forms of data that we can get, buffered and immediate data. Buffered data is a record of events that are saved (buffered) until the application requires them. Immediate data is the current state of the device. In this tutorial we will look at immediate data.
We have a new function called ProcessKeyboard which is called from our Render function. This means that ProcessKeyboard will be called once per frame. The code for ProcessKeyboard is shown below.
void CGame::ProcessKeyboard() {
char KeyboardState[256];
if (FAILED(m_pKeyboard->GetDeviceState(sizeof(KeyboardState),(LPVOID)&KeyboardState))) {
return;
}
if (KEYDOWN(KeyboardState, DIK_ESCAPE)) {
//Escape key pressed. Quit game.
m_fQuit = true;
}
//Rotate Earth
if (KEYDOWN(KeyboardState, DIK_RIGHT)) {
m_rRotate –= 0.5f;
} else if(KEYDOWN(KeyboardState, DIK_LEFT)) {
m_rRotate += 0.5f;
}
//Set an upper and lower limit for the rotation factor
if (m_rRotate < –20.0f) {
m_rRotate = –20.0f;
} else if(m_rRotate > 20.0f) {
m_rRotate = 20.0f;
}
//Scale Earth
if (KEYDOWN(KeyboardState, DIK_UP)) {
m_rScale += 0.5f;
} else if (KEYDOWN(KeyboardState, DIK_DOWN)) {
m_rScale –= 0.5f;
}
//Set an upper and lower limit for the scale factor
if (m_rScale < 1.0f) {
m_rScale = 1.0f;
} else if(m_rScale > 20.0f) {
m_rScale = 20.0f;
}
}
This function is pretty straightforward. At the start we call GetDeviceState which gets the current state of the keyboard device. The function call fills a char array with the key states of the keyboard. You can see from the function above, that once the array has been populated with key states, we can then use a simple macro called KEYDOWN to ascertain if a given key is in the down position. The KEYDOWN macro is shown below. So, based on certain key states, we manipulate some member variables that control scale and rotation.
#define KEYDOWN(name, key) (name[key] & 0x80)
As with the keyboard, we have a new function called ProcessMouse which is called from our Render function. This means that ProcessMouse will also be called once per frame. The code for ProcessMouse is shown below.
void CGame::ProcessMouse() {
DIMOUSESTATE MouseState;
if (FAILED(m_pMouse->GetDeviceState(sizeof(MouseState),(LPVOID)&MouseState))) {
return;
}
//Is the left mouse button down?
if (MOUSEBUTTONDOWN(MouseState.rgbButtons[MOUSEBUTTON_LEFT])) {
m_nMouseLeft = 1;
} else {
m_nMouseLeft = 0;
}
//Is the right mouse button down?
if (MOUSEBUTTONDOWN(MouseState.rgbButtons[MOUSEBUTTON_RIGHT])) {
m_nMouseRight = 1;
} else {
m_nMouseRight = 0;
}
m_nMouseX += MouseState.lX;
m_nMouseY += MouseState.lY;
}
So, to get access to the mouse data we make a call to GetDeviceState which fills a DIMOUSESTATE structure with data from the mouse. The DIMOUSESTATE structure has four members:
· lX: Distance the mouse has moved along the x-axis since the last call to GetDeviceState.
· lY: Distance the mouse has moved along the y-axis since the last call to GetDeviceState.
· lZ: Distance mouse wheel has moved since the last call to GetDeviceState.
· rgbButtons: Array of mouse button states.
lX, lY and lZ return the distance moved in device units (NOT pixels) since the last call to GetDeviceState. If you want to get the current screen position in pixels, you can use the Win32 GetCursorPos function. We use the macro MOUSEBUTTONDOWN to determine if a given mouse button is down or not. This macro is shown below along with some constants used for referring to mouse buttons.
#define MOUSEBUTTONDOWN(key) (key & 0x80)
#define MOUSEBUTTON_LEFT 0
#define MOUSEBUTTON_RIGHT 1
#define MOUSEBUTTON_MIDDLE 2
In our Render3D function, we use the data that we have retrieved from the keyboard and mouse to alter the scene. The Render3D function is shown below:
void CGame::Render3D() {
//Render our 3D objects
D3DXMATRIX matEarth, matScale, matRotate, matEarthRoll, matEarthMove;
float rMouseSpeed = 20.0f;
//If left mouse button is down, stop the earth from rotating
if (m_nMouseLeft == 1) {
m_rRotate = 0.0f;
}
//If right mouse button is down, start the earth rotating at default speed
if (m_nMouseRight == 1) {
m_rRotate = 2.0f;
}
m_rAngle += m_rRotate;
//Create the transformation matrices
D3DXMatrixRotationY(&matRotate, D3DXToRadian(m_rAngle));
D3DXMatrixScaling(&matScale, m_rScale, m_rScale, m_rScale);
D3DXMatrixRotationYawPitchRoll(&matEarthRoll, 0.0f, 0.0f, D3DXToRadian(23.44f));
D3DXMatrixTranslation(&matEarthMove, (m_nMouseX / rMouseSpeed), –(m_nMouseY / rMouseSpeed), 0.0f);
D3DXMatrixMultiply(&matEarth, &matScale, &matRotate);
D3DXMatrixMultiply(&matEarth, &matEarth, &matEarthRoll);
D3DXMatrixMultiply(&matEarth, &matEarth, &matEarthMove);
//Render our objects
m_pD3DDevice->SetTransform(D3DTS_WORLD, &matEarth);
m_dwTotalPolygons += m_pSphere->Render();
}
Here is a simple function that will unacquire the mouse and keyboard and clean up the Direct Input related pointers. This is called from the destructor of our CGame class as part of the games clean up process.
void CGame::CleanUpDirectInput() {
if (m_pKeyboard) {
m_pKeyboard->Unacquire();
}
if (m_pMouse) {
m_pMouse->Unacquire();
}
SafeRelease(m_pMouse);
SafeRelease(m_pKeyboard);
SafeRelease(m_pDirectInput);