C# 通過DirectInput 實現手柄操控
創作背景:近期,一友人希望使用手柄代替鍵盤玩格鬥遊戲,自行寫了款手柄鍵盤模擬器,結果在遊戲時無法正常執行,託在下看看。程式寫得不錯,手柄很準確地在記事本上輸出鍵盤鍵碼。
然而,進入遊戲,卻什麼反應都沒有。一個念頭閃現出來,遊戲的輸入是通過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人
言歸正傳,在.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);
用震動來按摩還真不錯,附上除錯好的原始碼,與各位同僚共勉: