|
|
这次主要介绍游戏控制器的使用,还有力反馈的实现。
上次有关DirectInput的教学,我并没有写完。这次我抽空把DirectInput的内容补充完整。
首先是DIpnut对象销毁的问题。在VCL环境里,你不用自己销毁COM对象。因为DX的对象都是COM对象,所以你就不用自己销毁DX对象(DInput包含在DX里,不要告诉我你不知道!)。上次我已经说过这个问题了,不过我说在程序结束时要调用Unacquire,经过测试,这一步是完全没有必要的。因此,DInput对象的释放,我们根本就不用写任何代码。 [em3]
另外,在设置键盘设备的合作级别时,可以设置为独占级别,这时无需指定屏蔽Windows键就能屏蔽所有Windows系统键(除了Alt+Tab和Ctrl+Alt+Del)
下面是这次的重点:游戏控制器!
我这里说的游戏控制器就是Windows控制面板里的游戏控制器。包括各种游戏手柄、方向盘、摇杆……,玩过游戏机的应该知道是什么吧。如果你连游戏机都没玩过,还是不要搞游戏编程了。
接下来,我以手柄为例,介绍如何得到它的数据,还有如何控制最让人兴奋的震动功能。
为了使用手柄,我们需要几个新的变量和函数:
joy_state : DIJOYSTATE2;
DInputJoy : IDirectInputDevice8;
DInputEffect : IDirectInputEffect; //用于实现震动
NumFFBAxis : DWORD; //Numbers of Force Feed Back Axis
//简单说,就是你的手柄有几个震动电机
nXForce,
nYForce : Integer; //震动的强弱
procedure SetFFB; //执行震动
注:这篇教学是基于上次的程序,我只修改了变量名称(去掉了 F 字头)。
然后我们要利用已有的DInput接口创建手柄设备。
创建手柄时,我们使用枚举函数,调用一个回调函数来创建设备:
//我们创建了一个支持力反馈的设备
DInput.EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoyCallback,
nil, DIEDFL_ATTACHEDONLY or DIEDFL_FORCEFEEDBACK);
回调函数的定义如下:
(注意:回调函数不能是类成员函数)
function EnumJoyCallback(var lpddi: DIDeviceInstance; pvRef: Pointer): BOOL;
begin
Form1.DInput.CreateDevice(lpddi.guidInstance, Form1.DInputJoy, nil);
//只创建第一个设备,然后结束枚举
Result := DIENUM_STOP;
end;
接着设置合作级别和数据格式,这些和键盘/鼠标类似
DInputJoy.SetCooperativeLevel(Self.Handle, DISCL_EXCLUSIVE or DISCL_FOREGROUND);
DInputJoy.SetDataFormat(c_dfDIJoystick2);
现在我们要看看手柄有几个震动电机了,还是一个枚举函数,又使用了一个回调函数
DInputJoy.EnumObjects(EnumAxesCallback, nil, DIDFT_AXIS);
回调函数的定义如下:
function EnumAxesCallback(var lpddoi: DIDeviceObjectInstance; pvRef : Pointer): BOOL; stdcall;
var
dirange : DIPROPRANGE;
begin
if (lpddoi.dwFlags and DIDOI_FFACTUATOR) <> 0 then
begin
Inc(Form1.NumFFBAxis);
end;
Result := DIENUM_CONTINUE;
end;
如果手柄有多个震动电机,我们只使用两个
if NumFFBAxis > 2 then
NumFFBAxis := 2;
我们还要设置一下手柄上摇杆的取值范围,还是刚才那个枚举函数,不过使用了另一个回调函数(我保证这是最后一个枚举函数!)
DInputJoy.EnumObjects(EnumObjCallback, nil, DIDFT_ALL);
最后一个回调函数的定义如下:
function EnumObjCallback(var lpddoi: DIDeviceObjectInstance; pvRef : Pointer): BOOL; stdcall;
var
dirange : DIPROPRANGE;
begin
if (lpddoi.dwType and DIDFT_AXIS) > 0 then
begin
dirange.diph.dwSize := SizeOf(DIPROPRANGE);
dirange.diph.dwHeaderSize := SizeOf(DIPROPHEADER);
dirange.diph.dwHow := DIPH_BYID;
dirange.diph.dwObj := lpddoi.dwType;
dirange.lMin := -1000;
dirange.lMax := +1000;
Form1.DInputJoy.SetProperty(DIPROP_RANGE, dirange.diph);
end;
Result := DIENUM_CONTINUE;
end;
接着(对,还没完)要通过刚才创建的手柄设备来生成一个效果对象,首先初始化几个变量
Axes[0] := DIJOFS_X;
Axes[1] := DIJOFS_Y;
Direction[0] := 0;
Direction[1] := 0;
cf.lMagnitude := 0;
ZeroMemory(@eff, SizeOf(eff) );
eff.dwSize := SizeOf(DIEFFECT);
eff.dwFlags := DIEFF_CARTESIAN or DIEFF_OBJECTOFFSETS;
eff.dwDuration := INFINITE;
eff.dwSamplePeriod := 0;
eff.dwGain := DI_FFNOMINALMAX;
eff.dwTriggerButton := DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval := 0;
eff.cAxes := NumFFBAxis;
eff.rgdwAxes := @Axes;
eff.rglDirection := @Direction;
eff.lpEnvelope := 0;
eff.cbTypeSpecificParams := SizeOf(DICONSTANTFORCE);
eff.lpvTypeSpecificParams := @cf;
eff.dwStartDelay := 0;
然后生成效果对象
DInputJoy.CreateEffect(GUID_ConstantForce, @eff, DInputEffect, nil);
初始化的最后一步终于到了!获得手柄设备:
DInputJoy.Acquire;
终于要动真格的了,我们要获取手柄的数据同时实现震动功能。
在获取键盘和鼠标数据的地方加入以下代码
//有些游戏控制器需要这个函数
DInputJoy.Poll;
//获取手柄数据
DInputJoy.GetDeviceState(SizeOf(DIJOYSTATE2), @joy_state);
//在得到摇杆数据的同时设置震动等级
nXForce := ABS(joy_state.lX) * 30 div 1000;
nYForce := ABS(joy_state.lY) * 30 div 1000;
//得到手柄按钮数据
for i := 0 to 127 do
begin
if joy_state.rgbButtons <> 0 then
begin
Self.Canvas.TextOut(0, 140, 'Joy-Buttons: ' + IntToStr(i));
end;
end;
//这是干什么用的呢?
SetFFB;
最后一个函数就是让手柄震动的关键。要看他的定义吗?去下载我这篇教学的完整源代码吧。 [em1]
相关内容的更多信息请参考微软的DX9SDK
以下是这篇教学的演示以及源代码下载地址:
http://www.box.net/shared/cxim446zhu |
|