1. 程式人生 > >C# 通過DirectInput 實現手柄操控

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);


        用震動來按摩還真不錯,附上除錯好的原始碼,與各位同僚共勉:

原始碼下載