Delphi for Games: Delphi游戏编程导学——DirectInput,转

这是我写的有关Delphi游戏编程的系列导学的第一篇。整个导学主要是根据我编程的经验来写的,所以发[来源:GameRes.com]布的时间是不固定的,内容也是不固定的。希望大家能见谅。

言归正传。
使用Delphi编写游戏,最大的麻烦恐怕就是和微软的DX打交道了。由于DX那个讨厌的COM接口,要把DX的头文件转换为Delphi版的,难度是比较大的。不过国外已经有人做完了这件事情,我们暂时不用担心了(不过DX10……)。
去 clootie.ru 可以下载到最新的Delphi版DX头文件
还有一个麻烦就是很多游戏演示的源代码,使用的都是纯Windows API。这让习惯了Delphi VCL编程的人感到很头痛。
难道非要使用原始的方法编程吗?在我苦苦寻找了许久之后(其实也没多久,而且也不怎么苦),得出了答案:完全可以的Delphi的VCL环境里编写任何的游戏程序!
要编写游戏,主要考虑的是图像,声音以及用户输入。那些基于GDI和WINMM的游戏当然不用说了,用Delphi实现只是小菜而已。OpenGL也可以,我以前的有关OpenGL的导学已经说过了。DirectDraw或Direct3D也可以用于Delphi,我上面提到的网站就有例子。
不过,那只是解决了图形的问题,声音和用户输入该怎么解决呢?如果游戏的计算不是很繁重,那么用户输入使用Windows的消息机制就可以了;如果不需要同时播放两个以上的声音,使用Windows的MCI系统就可以了。剩下的情况只好想办法解决了。


今天我先说个用户输入的解决办法。
目前在Windows平台似乎只能使用DirectInput来解决用户输入的问题了(还有别的吗?请告诉我)。要使用DInput,首先就需要DX的头文件,就是clootie.ru网站上的Delphi头文件。(不会用?看我以前的教学)
然后就是在程序的某个地方初始化DInput方式了。我建议在Activate事件里初始化,当然,如果你有更好的理由,也可以在其它事件里初始化。
首先声明几个变量
    FDInput          : IDirectInput8;
    FDInputKeyBoard,
    FDInputMouse     : IDirectInputDevice8;
一个DInput接口,两个DInput设备.下面是初始化的代码
//创建接口
DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, FDInput, nil);

//创建键盘
FDInput.CreateDevice(GUID_SysKeyBoard, FDInputKeyBoard, nil);
//设置接收的数据格式
FDInputKeyBoard.SetDataFormat(c_dfDIKeyBoard);
//设置合作级别
//这里可以屏蔽Windows键(就是Windows徽标键),但是需要设置合作级别为前景以及非独占(其实键盘只能设置为非独占)
//使命召唤系列一直没法屏蔽Windows键,真不知道他们的程序员是怎么学的DX
FDInputKeyBoard.SetCooperativeLevel(Self.Handle, DISCL_fOREGROUND OR DISCL_NONEXCLUSIVE or DISCL_NOWINKEY);
//获得键盘
FDInputKeyBoard.Acquire;

//创建鼠标
FDInput.CreateDevice(GUID_SysMouse, FDInputMouse, nil);
//设置接收的数据格式
FDInputMouse.SetDataFormat(c_dfDIMouse);
//设置合作级别
//我设置鼠标为前景,独占方式,嘿嘿......
FDInputMouse.SetCooperativeLevel(Self.Handle, DISCL_fOREGROUND OR DISCL_EXCLUSIVE);
//获得键盘
FDInputMouse.Acquire;

在VCL里使用COM接口有一个好处,就是不需要手动释放COM对象。所以DInput没有什么销毁代码。不过我还是多写了一句,以防万一
DInput的销毁
FDInputKeyBoard.Unacquire;
FDInputMouse.Unacquire;

下面是最重要的,如何获取数据呢?
DInput获取数据主要分为直接和缓存两种方式。我主要讲直接方式(因为最简单:))。
要在Delphi里获取DInput的数据,最好是在程序的OnIdle事件里。这样就能不断地轮询DInput的输入。
首先声明一个OnIdle函数
    procedure AppIdle(Sender: TObject; var Done: Boolean);
然后在程序初始化时设置,  Application.OnIdle := AppIdle;
接下来就可以在AppIdle函数里获取DInput输入了.代码如下:
//激活OnIdle事件
  Done := False;

//键盘消息
FDInputKeyBoard.GetDeviceState(256, @keyboard_state);
for i := 0 to 255 do
begin
  if keyboard_state[i] <> 0 then
    Self.Canvas.TextOut(0,0, '你按了一个键,它的代码是:' + IntToStr(i));
  end;
end;
//鼠标消息
FDInputMouse.GetDeviceState(SizeOf(DIMOUSESTATE), @mouse_state);
if (mouse_state.lX <> 0) or (mouse_state.lY <> 0) then
begin
  Self.Canvas.TextOut(0,20, '鼠标的X坐标移动量:' + IntToStr(mouse_state.lX));
  Self.Canvas.TextOut(0,40, '鼠标的Y坐标移动量:' + IntToStr(mouse_state.lY));
end;
if mouse_state.rgbButtons[0] <> 0 then
  Self.Canvas.TextOut(0,60, '你按了鼠标的左键');

关于如何在Delphi的VCL环境里使用DirectInput,我就教到这里了。有关DInput的更详细信息,请参考微软的DX9SDK。

最后是完整的DInput鼠标和键盘的演示程序
的下载地址
http://www.box.net/shared/cxim446zhu