Tuesday, 3 July 2012

Keyboard Input Using DirectInput

Not the most important thing I know, but wanted to add it in here before we move onto creating a camera and then rendering some geometry.
We are going to use DirectInput to manage the capture of the keyboard state, in the RandomchaosDX113DUtility.h header file we are going to add the following include
#include <dinput.h>
Now we will define a number of methods to our utility class, a number of public methods
    // Method to initialize the keyboard
    LPDIRECTINPUTDEVICE8 InitializeKeyboard(HWND hwnd);
    
    char* getKeyboardState();    

    // Update call
    void Update();
We are also going to create a protected method and a variable used to store the keyboard state, as well as a variable for the keyboard object.
    void ReadKeyboard();
    char KeyboardState[256];

    LPDIRECTINPUTDEVICE8 Keyboard;
Out header file should now look something like this
#pragma once
#include <Windows.h>
#include <D3D11.h>
#include <D3DX11.h>
#include <dinput.h>

class RandomchaosDX113DUtility
{
protected:
    void ReadKeyboard();
    char KeyboardState[256];

    LPDIRECTINPUTDEVICE8 Keyboard;

    D3D_DRIVER_TYPE driverType;
    IDXGISwapChain* pSwapChain;
    ID3D11Device*  pd3dDevice;
    D3D_FEATURE_LEVEL  featureLevel;
    ID3D11RenderTargetView* pRenderTargetView;
    ID3D11DepthStencilView* pDepthStencilView;
    ID3D11Texture2D* pDepthStencil;
    
    HWND hWnd;

public:
    RandomchaosDX113DUtility(void);
    ~RandomchaosDX113DUtility(void);

    // Method to initialize the window
    HWND InitWindow(LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height, WNDPROC WinProc, int colorBrush = COLOR_BTNFACE);

    // GraphicsDevice
    ID3D11DeviceContext* pImmediateContext;

    // Creates our graphics device
    HRESULT InitDevice();

    // Draw call
    void Draw();

    // Method to initialize the keyboard
    LPDIRECTINPUTDEVICE8 InitializeKeyboard(HWND hwnd);
    
    char* getKeyboardState();    

    // Update call
    void Update();
};
We now need to write up the function bodies in the RandomchaosDX113DUtility.cpp source file.
The first function will be the one used to initialize the keyboard
LPDIRECTINPUTDEVICE8 RandomchaosDX113DUtility::InitializeKeyboard(HWND hwnd)
{
    LPDIRECTINPUT8 p_dx_KeybObject;
    
    DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&p_dx_KeybObject, NULL);
    p_dx_KeybObject->CreateDevice(GUID_SysKeyboard, &Keyboard, NULL);

    Keyboard->SetDataFormat(&c_dfDIKeyboard);
    Keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);
    Keyboard->Acquire();

    return Keyboard;
}
Now the method to read the keyboard state
void RandomchaosDX113DUtility::ReadKeyboard()
{
    HRESULT res = Keyboard->GetDeviceState(sizeof(KeyboardState),(LPVOID)&KeyboardState);    
}
We also need a method to expose the keyboard state we have
char* RandomchaosDX113DUtility::getKeyboardState()
{
    return KeyboardState;
}
And finally we can add our update method
void RandomchaosDX113DUtility::Update()
{
    ReadKeyboard();
}
No, we want our game loop to be able to access and use the keyboard state, in the main.cpp. In our game loop above the Draw call we can add the Update method
            utils.Update();
We can now create a method in main.cpp to handle the keyboard input, in this method we are just going to take the input and if the escape key is hit we will stop the game loop.
void HandleKeyboardInput()
{
    if (utils.getKeyboardState()[DIK_ESCAPE]/128)
        int_AppRunning = 0;    
}
We’ll add the call to this after out utility Update, we also need to initialise the keyboard, we do that just before we initialize the device, our main.cpp will look like this
#include <Windows.h>
#include <D3D11.h>
#include <D3DX11.h>
#include "RandomchaosDX113DUtility.h"

RandomchaosDX113DUtility utils;

int int_AppRunning = 1;

LRESULT CALLBACK ourWinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch(message)
    {
        case WM_CLOSE:
        {
            int_AppRunning = 0;
            break;
        }
    }    
    return DefWindowProc(hWnd,message,wParam,lParam);
}

void HandleKeyboardInput()
{
    if (utils.getKeyboardState()[DIK_ESCAPE]/128)
        int_AppRunning = 0;    
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow)
{
    MSG msg_Message;    
    int result = 0;

    utils.hInstance = hInstance;

    HWND hWnd = utils.InitWindow(L"Test",0,0,800,420,ourWinProc);

    utils.InitializeKeyboard(hWnd);

    utils.InitDevice();

    if(hWnd != HWND(-1))
    {
        while(int_AppRunning)
        {
            if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
                DispatchMessage(&msg_Message);

            utils.Update();
            HandleKeyboardInput();            

            utils.Draw();
        }
    
        DestroyWindow(hWnd);     
    }
    return result;
}
If we build this now we will have link errors, we need to also include two libraries, or we get the following
image
To fix this we open up the project properties, and add dinput8.lib and dxguid.lib as we did before with d3d11.lib
image
We can now run the application without compile.linker errors, hitting the Escape key should result in the window closing.
In my next post Ill look at creating a camera class, using this keyboard input mechanism to control it and also render some simple geometry.
After that I am going to look at putting some sort of framework in place, me just writing stuff at random does not give a clear picture, so after we have a camera and have rendered something, we will look at creating something like the Game class we have in XNA, a GraphicsDevice class, SpriteBatch (I know Shawn has some good stuff for this) and, dare I say it, something like Components :)
As ever, comments are more than welcome..

2 comments:

  1. where did the utils.hInstance = hInstance line come from? this class doesn't have any field like that...

    ReplyDelete
    Replies
    1. Sorry, I should have removed that line, ignore it.

      Delete