创作背景:近期,一友人希望使用手柄代替键盘玩格斗游戏,自行写了款手柄键盘模拟器,结果在游戏时无法正常运行,托在下看看。程序写得不错,手柄很准确地在记事本上输出键盘键码。
然而,进入游戏,却什么反应都没有。一个念头闪现出来,游戏的输入是通过directinput实现的,比WINDOWS API 更接近底层,这样可以赢取更短的响应时间,程序需向directinput改进,然而这里不得不说下:上网搜索大小网站都是那篇API 实现手柄信息获取的文章,和友人的代码90%的相似度,文章写得很好说真的,但全是这篇就有点过了。国内没有要找的就找下国外吧,百度的确有些害人,因为太懂中文,国外的directinput一张都没有,立马换了搜索页,2小时就用directinput完成了手柄信息获取,然而殊不知接下来的震动功能花费了4天时间…
完成后界面:
功能目标: 1、获取手柄方向、按键信息;2、使手柄震动
注意,本程序只是通过DX发送震动信息,并不是什么万能手柄驱动啊。
测试手柄震动前,请确认使用的是震动手柄并安装了手柄驱动。
开发环境:Win7 (DX10传说中的DX11类库我没有找到,结果项目建了.NET 3.5)
Microsoft Visual Studio 2010
数据库无
代码在1920 X 1080 分辨率下无过长换行
编写人数 1人
参考资料:MSDN资料库
言归正传,在.NET的高封装的环境下,directinput的使用简化了许多,不了解COM的朋友,甚至是不知道句柄、指针的新手也可以轻易掌握。
思路与实现:首先,我们要计算机找到我们的摇杆设备
foreach (DeviceInstance info in Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly))
{
Device myJoy = new Device(info.InstanceGuid);
}
然后控制设备,过去用c++的朋友很熟悉了,申请操作级别,这个真的很重要,在最后测试震动报了“没有独占打开无法操作”的异常,单步了整整一天,才发现是这里设错参了。
myJoy.SetCooperativeLevel(null, CooperativeLevelFlags.Background | CooperativeLevelFlags.Exclusive);
再设置其他参数
myJoy.Properties.AxisModeAbsolute = true;
myJoy.Properties.AutoCenter = false;
myJoy.Acquire();
int[] axis = null;
foreach (DeviceObjectInstance doi in myJoy.Objects)
{
if ((doi.ObjectId & (int)DeviceObjectTypeFlags.Axis) != 0)
{
myJoy.Properties.SetRange(ParameterHow.ById, doi.ObjectId, new InputRange(-128, 128));
}
}
好了,目标1完成,很快是不?手柄的状态已经在myJoy. CurrentJoystickState下了,通过
myJoy. CurrentJoystickState. ToString() 你可以查看到摇杆状态(微软大费苦心啊,左摇杆、右摇杆、光枪,压杆,基本上能想到的有位移的操作杆都有了)
allJoystick.Joysticks[i].CurrentJoystickState.GetButtons()可以得到按下按钮组合的数组
好,开始进军震动了。震动不同于按键捕捉、不同于模拟键盘、鼠标击键,因为这些计算机都是作为信息的接受方,然而这次是手柄作为接受方。也与一些挂起、响应的程序不同,挂起的程序用于监听端口,当有数据流后运算后反馈硬件。
例如,我进入一款格斗游戏后不对手柄进行任何输入,时间到后自动选人开打,在被CPU攻击时手柄是有震感的。
也就是说震动指令是由计算机发起的,当时还真想用汇编给它来一段,装了个Bus Hound 5.0 抓包结果Win7蓝屏 花了1个多小时恢复。
扯远了,手柄的震动是像声音一样播放的,看参考资料,资料中例举了使用SDK下的录制好的震动文件来驱动,国外一达人说该函数有BUG,刚好我又不想下载几百兆的SDK。故选择了最后一种方式,现场定制(录制)、现场播放。
代码不难,调试却很罗嗦。
//震动类型
public enum ForceType
{
VeryBriefJolt,
BriefJolt,
LowRumble,
HardRumble
}
//录制函数,照抄参考资料
public static EffectObject InitializeForce(Device Dev, EffectType Type, int[] Axis, int Magnitude, EffectFlags Flags, int Duration)
{
EffectObject eo = null;
Effect e;
foreach (EffectInformation ei in Dev.GetEffects(EffectType.All))
{
if (DInputHelper.GetTypeCode(ei.EffectType) == (int)Type)
{
e = new Effect();
e.SetDirection(new int[Axis.Length]);
e.SetAxes(new int[1]);
e.EffectType = Type;
e.ConditionStruct = new Condition[Axis.Length];
e.Duration = Duration;
e.Gain = 10000;
e.Constant = new ConstantForce();
e.Constant.Magnitude = Magnitude;
e.SamplePeriod = 0;
e.TriggerButton = (int)Microsoft.DirectX.DirectInput.Button.NoTrigger;
e.TriggerRepeatInterval = (int)DI.Infinite;
e.Flags = Flags;
e.UsesEnvelope = false;
eo = new EffectObject(ei.EffectGuid, e, Dev);
}
}
return eo;
}
//播放
InitializeForce(myJoy, EffectType.ConstantForce, axis, 10000, EffectFlags.ObjectOffsets | EffectFlags.Spherical, 2000000).start(1);
用震动来按摩还真不错,附上调试好的源码,与各位同僚共勉: