游戏项目第9部分:增加输入系统
该游戏项目将添加对引擎和主菜单的支持。引擎项目中出现的三个新文件分别是InputInterface.h、DirectInput.h和DirectInput.cpp。
第一个文件InputInterface.h用于定义输入系统和输入设备基类的头文件。每个类都提供了派生类需要实现的默认函数。基类中的每个成员函数都是从DirectInput演示程序中提取出来的相同类。实际上,所有这些代码都很相似。在该输入系统和为演示程序创建的输入系统之间的唯一差别在于该输入系统将事情按照各自的类分开处理,而不是包含在一个巨大的类中一起处理。
InputInterface.h
#ifndef _UGP_INPUT_INTERFACE_H_
#define _UGP_INPUT_INTERFACE_H_
// 输入系统基类 class CInputInterface
{
public:
CInputInterface(){}
virtual ~CInputInterface(){}
virtual bool Initialize() = 0;
virtual bool UpdateDevices() = 0;
virtual int KeyUp(unsigned int key) = 0;
virtual int KeyDown(unsigned int key) = 0;
virtual int MouseButtonUp(unsigned int button) = 0;
virtual int MouseButtonDown(unsigned int button) = 0;
virtual POINT GetMousePos() = 0;
virtual long GetMouseWheelPos() = 0;
virtual int ControllerButtonUp(unsigned int button) = 0;
virtual int ControllerButtonDown(unsigned int button) = 0;
virtual POINT GetLeftStickPos() = 0;
virtual POINT GetRightStickPos() = 0;
virtual void Shutdown() = 0;
};
// 输入设备基类 class CDeviceInterface
{
public:
CDeviceInterface() {}
virtual ~CDeviceInterface(){}
virtual bool UpdateDevice() = 0;
virtual int ButtonUp(unsigned int key) = 0;
virtual int ButtonDown(unsigned int key) = 0;
virtual POINT GetPosition() = 0;
virtual POINT GetZPosition() = 0;
virtual void Shutdown() = 0;
};
#endif
接下来要介绍的头文件是DirectInput.h。该头文件创建DirectInput类,而DirectInput.cpp实现所有类的函数。定义的第一个类是CKeyboard,后面分别是CMouse和CGameController,最后一个是CDirectInputSystem。
#ifndef _UGP_DIRECTINPUT_H_
#define _UGP_DIRECTINPUT_H_
#include<dinput.h>
#include"InputInterface.h"
#include"defines.h"
class CKeyboard : public CDeviceInterface
{
public:
CKeyboard(LPDIRECTINPUT8 input, HWND hwnd);
~CKeyboard() { Shutdown(); }
bool UpdateDevice();
int ButtonUp(unsigned int key);
int ButtonDown(unsigned int key);
POINT GetPosition();
POINT GetZPosition();
void Shutdown();
// Keyboard device.
LPDIRECTINPUTDEVICE8 m_device;
char m_keys[KEYS_SIZE];
char m_oldKeys[KEYS_SIZE];
};
class CMouse : public CDeviceInterface
{
public:
CMouse(LPDIRECTINPUT8 input, HWND hwnd, bool exclusive);
~CMouse() { Shutdown(); }
bool UpdateDevice();
int ButtonUp(unsigned int key);
int ButtonDown(unsigned int key);
POINT GetPosition();
POINT GetZPosition();
void Shutdown();
// Mouse device.
LPDIRECTINPUTDEVICE8 m_device;
DIMOUSESTATE m_mouseState;
DIMOUSESTATE m_oldMouseState;
bool m_button[3];
// Mouse x, y, and wheel position.
long m_xMPos;
long m_yMPos;
long m_zMPos;
// Max x (width) and y (height)
// Restrict means to cap at width/height.
bool m_restrict;
int m_width;
int m_height;
};
class CGameController : public CDeviceInterface
{
public:
CGameController(LPDIRECTINPUT8 input, HWND hwnd);
~CGameController() { Shutdown(); }
bool UpdateDevice();
// Used to create the game controllers.
BOOL EnumDeviceCallBack(const DIDEVICEINSTANCE *inst, void* pData);
int ButtonUp(unsigned int key);
int ButtonDown(unsigned int key);
POINT GetPosition();
POINT GetZPosition();
void Shutdown();
// Game controller device.
LPDIRECTINPUTDEVICE8 m_device;
DIJOYSTATE2 m_gcState;
DIJOYSTATE2 m_oldGCState;
char m_name[256];
unsigned long m_numButtons;
// Left and right stick x and y positions.
long m_xGCPos;
long m_yGCPos;
long m_xGCPos2;
long m_yGCPos2;
// Window handled (needed for game controllers).
HWND m_hwnd;
// Copy of input system.
LPDIRECTINPUT8 m_inputSystem;
};
class CDirectInputSystem : public CInputInterface
{
public:
CDirectInputSystem(HWND hwnd, HINSTANCE hInst, bool exclusive);
~CDirectInputSystem();
// Init devics, update devices states.
bool Initialize();
bool UpdateDevices();
// Keyboard functions.
int KeyUp(unsigned int key);
int KeyDown(unsigned int key);
// Mouse functions.
int MouseButtonUp(unsigned int button);
int MouseButtonDown(unsigned int button);
// Get mouse position (x, y) and mouse wheel data (z).
POINT GetMousePos();
long GetMouseWheelPos();
// Game controller functions.
int ControllerButtonUp(unsigned int button);
int ControllerButtonDown(unsigned int button);
// Get controller main (left) and right stick position.
POINT GetLeftStickPos();
POINT GetRightStickPos();
void Shutdown();
protected:
LPDIRECTINPUT8 m_system;
// Devices.
CKeyboard *m_keyboard;
CMouse *m_mouse;
CGameController *m_gameController;
};
bool CreateDIInput(CInputInterface **pObj, HWND hwnd, HINSTANCE hInst, bool exclusive);
#endif
#include"DirectInput.h"
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
#define MOUSE_BUFF_SIZE 16
CGameController *gThis = NULL;
bool CreateDIInput(CInputInterface **pObj, HWND hwnd, HINSTANCE hInst, bool exclusive)
{
if(!*pObj)
*pObj = new CDirectInputSystem(hwnd, hInst, exclusive);
else
return false;
return true;
}
BOOL gJSEnumDeviceCallBack(const DIDEVICEINSTANCE *inst, void* pData)
{
return gThis->EnumDeviceCallBack(inst, pData);
}
CKeyboard::CKeyboard(LPDIRECTINPUT8 input, HWND hwnd)
{
// Initialize the keyboard.
if(input->CreateDevice(GUID_SysKeyboard, &m_device, NULL) == DI_OK)
{
if(m_device->SetDataFormat(&c_dfDIKeyboard) == DI_OK)
{
if(m_device->SetCooperativeLevel(hwnd,
DISCL_FOREGROUND |DISCL_NONEXCLUSIVE) == DI_OK)
{
m_device->Acquire();
}
}
}
// Clear keys will clear out the array of keys we have.
memset(m_keys, 0, sizeof(m_keys));
}
int CKeyboard::ButtonUp(unsigned int key)
{
// If the key in the variable is not pressed then return false.
return (!(m_keys[key] & 0x80) && m_keys[key] != m_oldKeys[key]);
}
int CKeyboard::ButtonDown(unsigned int key)
{
// If the key in the variable is not pressed then return false.
return m_keys[key] & 0x80;
}
POINT CKeyboard::GetPosition()
{
// Doesn't have position.
POINT p = { 0, 0 };
return p;
}
POINT CKeyboard::GetZPosition()
{
// Doesn't have position.
POINT p = { 0, 0 };
return p;
}
bool CKeyboard::UpdateDevice()
{
if(m_device)
{
// Save old state for input comparing.
memcpy(m_oldKeys, m_keys, sizeof(m_keys));
// If error getting device state, re-aquire.
if(FAILED(m_device->GetDeviceState(sizeof(m_keys), (LPVOID)m_keys)))
{
if(FAILED(m_device->Acquire())) return false;
if(FAILED(m_device->GetDeviceState(sizeof(m_keys), (LPVOID)m_keys)))
return false;
}
}
return true;
}
void CKeyboard::Shutdown()
{
if(m_device)
{
m_device->Unacquire();
m_device->Release();
m_device = NULL;
}
}
CMouse::CMouse(LPDIRECTINPUT8 input, HWND hwnd, bool exclusive)
{
// Initialize the Mouse.
DWORD flags;
if(input->CreateDevice(GUID_SysMouse, &m_device, NULL) == DI_OK)
{
if(m_device->SetDataFormat(&c_dfDIMouse) == DI_OK)
{
if(exclusive) flags = DISCL_FOREGROUND | DISCL_EXCLUSIVE | DISCL_NOWINKEY;
else flags = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
if(m_device->SetCooperativeLevel(hwnd, flags) == DI_OK)
{
m_device->Acquire();
}
}
}
}
int CMouse::ButtonUp(unsigned int button)
{
// If the button is not clicked we return false.
return (!(m_mouseState.rgbButtons[button] & 0x80) &&
m_mouseState.rgbButtons[button] != m_oldMouseState.rgbButtons[button]);
}
int CMouse::ButtonDown(unsigned int button)
{
// If the button is clicked we return true.
return m_mouseState.rgbButtons[button] & 0x80;
}
bool CMouse::UpdateDevice()
{
// Get the device state.
if(m_device)
{
// Save old state for input comparing.
memcpy(&m_oldMouseState, &m_mouseState, sizeof(m_mouseState));
// If error getting device state, re-aquire.
if(FAILED(m_device->GetDeviceState(sizeof(DIMOUSESTATE), &m_mouseState)))
{
if(FAILED(m_device->Acquire())) return false;
if(FAILED(m_device->GetDeviceState(sizeof(DIMOUSESTATE), &m_mouseState)))
return false;
}
m_xMPos += m_mouseState.lX;
m_yMPos += m_mouseState.lY;
m_zMPos = m_mouseState.lZ;
}
return true;
}
POINT CMouse::GetPosition()
{
POINT pos;
pos.x = m_xMPos;
pos.y = m_yMPos;
return pos;
}
POINT CMouse::GetZPosition()
{
POINT p = { m_zMPos, m_zMPos };
return p;
}
void CMouse::Shutdown()
{
if(m_device)
{
m_device->Unacquire();
m_device->Release();
m_device = NULL;
}
}
CGameController::CGameController(LPDIRECTINPUT8 input, HWND hwnd)
{
// Save copies.
gThis = this;
m_hwnd = hwnd;
m_inputSystem = input;
// Initialize the game controller.
DIPROPRANGE range;
DIDEVCAPS caps;
m_inputSystem->EnumDevices(DI8DEVCLASS_GAMECTRL, (LPDIENUMDEVICESCALLBACK)gJSEnumDeviceCallBack,
NULL, DIEDFL_ATTACHEDONLY);
if(m_device)
{
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYOFFSET;
range.lMin = -1000;
range.lMax = 1000;
range.diph.dwObj = DIJOFS_X;
m_device->SetProperty(DIPROP_RANGE, &range.diph);
range.diph.dwObj = DIJOFS_Y;
m_device->SetProperty(DIPROP_RANGE, &range.diph);
if(SUCCEEDED(m_device->GetCapabilities(&caps))) m_numButtons = caps.dwButtons;
else m_numButtons = 4;
}
}
BOOL CGameController::EnumDeviceCallBack(const DIDEVICEINSTANCE *inst, void* pData)
{
// Set to the first device found.
if(SUCCEEDED(m_inputSystem->CreateDevice(inst->guidInstance, &m_device, NULL)))
{
if(SUCCEEDED(m_device->SetDataFormat(&c_dfDIJoystick2)))
if(SUCCEEDED(m_device->SetCooperativeLevel(m_hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
if(SUCCEEDED(m_device->Acquire()))
{
strcpy(m_name, (char*)inst->tszProductName);
return DIENUM_STOP;
}
}
// Return continue to try to init other connected devices.
return DIENUM_CONTINUE;
}
int CGameController::ButtonUp(unsigned int button)
{
if(button < 0 || button >= m_numButtons) return 0;
return (!(m_gcState.rgbButtons[button] & 0x80) &&
m_gcState.rgbButtons[button] != m_oldGCState.rgbButtons[button]);
}
int CGameController::ButtonDown(unsigned int button)
{
if(button < 0 || button >= m_numButtons) return 0;
return m_gcState.rgbButtons[button] & 0x80;
}
bool CGameController::UpdateDevice()
{
if(m_device)
{
m_device->Poll();
// Save old state for input comparing.
memcpy(&m_oldGCState, &m_gcState, sizeof(m_gcState));
// If error getting device state, re-aquire.
if(FAILED(m_device->GetDeviceState(sizeof(DIJOYSTATE2), &m_gcState)))
{
if(FAILED(m_device->Acquire())) return false;
if(FAILED(m_device->GetDeviceState(sizeof(DIJOYSTATE2), &m_gcState)))
return false;
}
m_xGCPos = m_gcState.lX;
m_yGCPos = m_gcState.lY;
m_xGCPos2 = m_gcState.lZ;
m_yGCPos2 = m_gcState.lRz;
}
return true;
}
POINT CGameController::GetPosition()
{
POINT pos;
pos.x = m_xGCPos;
pos.y = m_yGCPos;
return pos;
}
POINT CGameController::GetZPosition()
{
POINT pos;
pos.x = m_xGCPos2;
pos.y = m_yGCPos2;
return pos;
}
void CGameController::Shutdown()
{
if(m_device)
{
m_device->Unacquire();
m_device->Release();
m_device = NULL;
}
}
CDirectInputSystem::CDirectInputSystem(HWND hwnd, HINSTANCE hInst, bool exclusive)
{
// Initialize objects...
m_keyboard = NULL;
m_mouse = NULL;
m_gameController = NULL;
// Create input system.
if(DirectInput8Create(hInst, DIRECTINPUT_VERSION,
IID_IDirectInput8, (void **)&m_system, NULL) == DI_OK)
{
m_keyboard = new CKeyboard(m_system, hwnd);
m_mouse = new CMouse(m_system, hwnd, exclusive);
m_gameController = new CGameController(m_system, hwnd);
}
}
CDirectInputSystem::~CDirectInputSystem()
{
// Shut everything down.
Shutdown();
}
bool CDirectInputSystem::Initialize()
{
// Everything took place in the constructor.
// Force keyboard and mouse before considering the
// system initialized.
return (m_keyboard && m_mouse);
}
bool CDirectInputSystem::UpdateDevices()
{
int hr;
// Get the device state.
if(m_mouse) hr = m_mouse->UpdateDevice();
if(m_keyboard) hr = m_keyboard->UpdateDevice();
if(m_gameController) hr = m_gameController->UpdateDevice();
return true;
}
int CDirectInputSystem::KeyUp(unsigned int key)
{
if(!m_keyboard) return 0;
return m_keyboard->ButtonUp(key);
}
int CDirectInputSystem::KeyDown(unsigned int key)
{
if(!m_keyboard) return 0;
return m_keyboard->ButtonDown(key);
}
int CDirectInputSystem::MouseButtonUp(unsigned int button)
{
if(!m_mouse) return 0;
return m_mouse->ButtonUp(button);
}
int CDirectInputSystem::MouseButtonDown(unsigned int button)
{
if(!m_mouse) return 0;
return m_mouse->ButtonDown(button);
}
int CDirectInputSystem::ControllerButtonUp(unsigned int button)
{
if(!m_gameController) return 0;
return m_gameController->ButtonUp(button);
}
int CDirectInputSystem::ControllerButtonDown(unsigned int button)
{
if(!m_gameController) return 0;
return m_gameController->ButtonDown(button);
}
POINT CDirectInputSystem::GetMousePos()
{
POINT null = {0, 0};
if(!m_mouse) return null;
return m_mouse->GetPosition();
}
long CDirectInputSystem::GetMouseWheelPos()
{
if(!m_mouse) return 0;
POINT wheel = m_mouse->GetZPosition();
return wheel.y;
}
POINT CDirectInputSystem::GetLeftStickPos()
{
POINT null = {0, 0};
if(!m_gameController) return null;
return m_gameController->GetPosition();
}
POINT CDirectInputSystem::GetRightStickPos()
{
POINT null = {0, 0};
if(!m_gameController) return null;
return m_gameController->GetZPosition();
}
void CDirectInputSystem::Shutdown()
{
// Delete each object...
if(m_keyboard)
{
m_keyboard->Shutdown();
delete m_keyboard;
m_keyboard = NULL;
}
if(m_mouse)
{
m_mouse->Shutdown();
delete m_mouse;
m_mouse = NULL;
}
if(m_gameController)
{
m_gameController->Shutdown();
delete m_gameController;
m_gameController = NULL;
}
if(m_system)
{
m_system->Release();
m_system = NULL;
}
}
游戏项目中还包含了DirectInput的头文件和源文件。在源文件中的g_Render后面添加了一个全局指针变量,用于实现输入检测。程序清单10.28给出了main源文件全局部分的完整代码。同渲染系统一样,输入系统使用的是基类。初始化引擎时,要为所要使用的输入系统分配内存。
#include"main.h"
// 窗口句柄
HWND g_hwnd;
HINSTANCE g_hInstance; // 程序的当前实例的句柄
CInputInterface *g_InputSystem = NULL; // 输入系统的抽象基类
// 渲染基类的对象指针 g_Render
CRenderInterface* g_Render = NULL;
// GUI控件ID
int g_mainGui = -1; // 主菜单界面
int g_startGui = -1; // 开始菜单界面
int g_creditsGui = -1; // 字幕菜单界面
int g_currentGUI = GUI_MAIN_SCREEN; // 当前菜单界面
// 字体类型ID
int g_arialID = -1;
// 鼠标的状态信息
bool LMBDown = false; // 光标左键是否按下
bool LMBUp = false; // 鼠标左键是否弹起
int mouseX = 0, mouseY = 0; // 鼠标指针位置(X和Y)
// 创建渲染系统对象,并初始化Direct3D
bool InitializeEngine()
{
// g_Render指向创建的渲染系统抽象基类对象
if (! CreateD3DRenderer(&g_Render) )
{
return false;
}
// 渲染系统初始化Direct3D
if (! g_Render->Initialize(WIN_WIDTH, WIN_HEIGHT, g_hwnd, FULLSCREEN, UGP_MS_SAMPLES_4) )
{
return false;
}
// 设置后台缓存的指定清除色
g_Render->SetClearCol(0, 0, 0);
// 为要显示的文本创建Arial字体
if (! g_Render->CreateText("Arial", 0, true, 18, g_arialID) )
{
return false;
}
// 动态开辟输入系统CDirectInputSystem类对象
if (! CreateDIInput(&g_InputSystem, g_hwnd, g_hInstance, false) )
{
return false;
}
// 输入系统初始化
if (! g_InputSystem->Initialize() )
{
return false;
}
return true;
}
// 引擎的相关内存的销毁工作
void ShutdownEngine()
{
// 释放渲染系统
if (g_Render)
{
g_Render->Shutdown();
delete g_Render;
g_Render = NULL;
}
// 释放输入系统
if(g_InputSystem)
{
g_InputSystem->Shutdown();
delete g_InputSystem;
g_InputSystem = NULL;
}
}