对游乐手柄的编制程序开采-API篇(1),那样不就能够动用游戏手柄来玩Flash游戏了啊

如今花38元从网络买了风流洒脱对北通的USB游戏手柄,那样周天与晚间的闲散时光就足以玩玩孩儿时的SFC与街机模拟游戏了。图片 1

追思“被动方式”开拓

C#对游戏手柄的编制程序开采-API篇(1)那篇小说中大家介绍了“被动格局”的支付。在那方法下,我们的次第只扮演一个音讯选取者。系统会准时报告大家有些游戏手柄当前的图景,大家的顺序选用到后再按实际必要开展拍卖就可以。但大器晚成旦您是三个细致的人,你会意识只要直接按音讯事件管理的话会存在二个标题,如我辈按下某些键(譬如发展的方向键卡塔尔国然后放大时,对于大家“人”来讲,我们按下与弹起的那四个动作应该只是表明大家只点击那个按键一次。但对此系统来讲,它只是机械地按时通报大家的次序在某个时刻内游戏手柄的相继按键的意况,而在大家按下到弹起最近内,系统有十分大希望已经传递了N次的音讯公告(N值依照捕捉时设置的uPeriod值与你的按钮速度来决定卡塔 尔(英语:State of Qatar),通告手柄有按键处于被按下境况,而只要大家就依照音讯包间接管理点击事件的话,就可以招致难点现身(例如在有个别游戏中,大家设计的是当点击一遍手柄的右键,就将角色向前挪动一步。但从我们按下按键到弹开此开关这段时光,由于人的反应速度远远慢于Computer的管理速度,所以这段非常的短的年华内,系统也许已文告了11遍以上的新闻包申明游戏手柄右键已被按下,那就产生我们按二次右键,游戏中的角色却有非常大大概已移动了十步之多,那可不是大家想要的结果卡塔尔。那我们要什么样管理那几个“点击”事件能力够制止再度文告呢?那就是本篇最终要器重解说的原委了……

在讲课那个难题的减轻方法早前我们再来说解一下上文还关乎的黄金年代种开拓格局。

 

 

“主动格局”的支出

责无旁贷方式即我们没有必要向系统报名注册捕捉某些游戏手柄,大家只是依据自身的须求限时去获得游戏手柄的情况音信

当时大家将要动用以下的API函数。

/// <summary>
            /// 获取操纵杆位置和按钮状态
            /// </summary>
            /// <param name="uJoyID"></param>
            /// <param name="pji"></param>
            /// <returns></returns>
            [DllImport("winmm.dll")]
            public static extern int joyGetPos(int uJoyID, ref JOYINFO pji);
            /// <summary>
            /// 获取操纵杆位置和按钮状态
            /// </summary>
            /// <param name="uJoyID"></param>
            /// <param name="pji"></param>
            /// <returns></returns>
            [DllImport("winmm.dll")]
            public static extern int joyGetPosEx(int uJoyID, ref JOYINFOEX pji);

 

下边包车型地铁三个API函数,大家能够从当中任选三个,但joyGetPos函数只好获得1,2,3,4号多少个按键的情形。所以提出不用,上边只重讲明joyGetPosEx函数

JOYINFO 与 JOYINFOEX 是属于结构体,它们的定义如下:
#region 游戏手柄的位置与按钮状态
            /// <summary>
            /// 游戏手柄的位置与按钮状态
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            public struct JOYINFO
            {
            public int wXpos;
            public int wYpos;
            public int wZpos;
            public int wButtons;
            }
            /// <summary>
            /// 游戏手柄的位置与按钮状态
            /// </summary>
            [StructLayout(LayoutKind.Sequential)]
            public struct JOYINFOEX
            {
            /// <summary>
            /// Size, in bytes, of this structure.
            /// </summary>
            public int dwSize;
            /// <summary>
            /// Flags indicating the valid information returned in this structure. Members that do not contain valid information are set to zero.
            /// </summary>
            public int dwFlags;
            /// <summary>
            /// Current X-coordinate.
            /// </summary>
            public int dwXpos;
            /// <summary>
            /// Current Y-coordinate.
            /// </summary>
            public int dwYpos;
            /// <summary>
            /// Current Z-coordinate.
            /// </summary>
            public int dwZpos;
            /// <summary>
            /// Current position of the rudder or fourth joystick axis.
            /// </summary>
            public int dwRpos;
            /// <summary>
            /// Current fifth axis position.
            /// </summary>
            public int dwUpos;
            /// <summary>
            /// Current sixth axis position.
            /// </summary>
            public int dwVpos;
            /// <summary>
            /// Current state of the 32 joystick buttons. The value of this member can be set to any combination of JOY_BUTTONn flags, where n is a value in the range of 1 through 32 corresponding to the button that is pressed.
            /// </summary>
            public int dwButtons;
            /// <summary>
            /// Current button number that is pressed.
            /// </summary>
            public int dwButtonNumber;
            /// <summary>
            /// Current position of the point-of-view control. Values for this member are in the range 0 through 35,900. These values represent the angle, in degrees, of each view multiplied by 100.
            /// </summary>
            public int dwPOV;
            /// <summary>
            /// Reserved; do not use.
            /// </summary>
            public int dwReserved1;
            /// <summary>
            /// Reserved; do not use.
            /// </summary>
            public int dwReserved2;
            }
            #endregion

 

如大家运用joyGetPosEx获取游戏设备的图景时,必须先起始化JOYINFOEX结构实例,并要设置dwSize参数的值,也正是JOYINFOEX结构体所据有的内部存款和储蓄器空间大小(其值可由此马尔斯hal.SizeOf求得卡塔尔。而生机勃勃旦要收获游戏设备的其他参数,则还必需求设置dwFlags参数的值!不然只好获得坐标值(dwXPos)。如对游乐手柄来讲我们必要获得其余按键的境况,则设置dwFlags的值为JOY_RETU奇骏NBUTTONS,用于提示大家必要回到全体开关的气象。

演示代码:

JoystickAPI.JOYINFOEX infoEx = new JoystickAPI.JOYINFOEX();
            infoEx.dwSize = Marshal.SizeOf(typeof(JoystickAPI.JOYINFOEX));
            infoEx.dwFlags = (int)JoystickAPI.JOY_RETURNBUTTONS;
            int result = JoystickAPI.joyGetPosEx(this.Id, ref infoEx);

只要joyGetPosEx函数获取手柄状态数据成功,则赶回JOYE宝马7系中华V_NOETucsonRORubicon(值为0),不然重回其余值的话代表收获失利。

当数码获得成功后,对应的玩耍手柄的图景数据都已经囤积在JOYINFOEX结构实例中了。如要判别是不是按下了方向键,则可看清dwXPos与dwYPos的值;而判定是还是不是按了其他开关,则可剖断dwButtons的值。推断情势在上意气风发章中有讲,这里就不再细说,也许也足以看前边提供的源码。

 

因为“主动格局”的“时间效果与利益性”独有三遍,所以为了能够时刻监视到娱乐手柄的按钮事件,就非得进行“轮循”获取,当监视到娱乐手柄有开关发生时就张开事件通报(噫?好像“被动形式”?嗯,其实当大家向系统报名捕捉有个别游戏手柄时,系统最后也是在帮我们开展“轮循”操作!卡塔尔。而贯彻“轮循”的章程则能够有七种主意,比如利用独立的线程进行叁个死循环;或许使用Timer举行准时实践。

 

但当我们的操作步入“轮循”后,假若也是直接joyGetPostEx就管理的话也同等会赶过篇头所说的不胜倒霉难点图片 2
!因为无论是“主动情势”依然“被动格局”没什么不相同只可以拿到游戏手柄按键当前的图景(按下或未按下卡塔 尔(阿拉伯语:قطر‎。那怎么废除呢?

 

某日在有个别网址上玩一个Flash游戏时,猛然想到,假如也能使用手柄来玩Flash游戏,那该多爽图片 3
。顾忌痛的是,方今的Flash都以不支持对游乐手柄举行编制程序,那难免是Flash中的三个缺憾。。

化解开关重复状态的难题

缓慢解决这么些难题,要是理清了思路,其实也是相当轻便的办法。

大家经过API获得的是娱乐手柄按键当前的情状(被按下或未按下卡塔 尔(英语:State of Qatar)。由此大家得以在“轮循”里,每当监视到娱乐手柄在某次时间有少数按键是处于“按下”状态时,就记下本次被按下的按键号,那样立即二次“轮循”操作时,要是也监视到有开关按下,则通过与上二遍按下的开关相比,要是照旧同样的开关,则申明这一次开关依旧三番四次上次的按下情形,那就不再要求向程序里爆发音信通告了。而只要不近似,则发出新的开关开关文告,并记下此番按下的开关号。

伪代码如下:

previousButtons = 无;
//死循环,进入轮循
while(true){
       if(joyGetPosEx(手柄号,ref joyInfo) == 成功){
              JoyButtons buttons = 取得当前按下的按钮(joyInfo);
              if(buttons != 无){
                    if(buttons != previousButtons){
                           //本次按下的按钮不同于上次按下的按钮.所以进行通知
                           OnClick(buttons);
                           //记录本次按下的按钮
                           previousButtons = buttons;
                    }
              }
       }
       暂停uPeriod毫秒;
}

透过那样的处理后,每按二遍手柄的按键我们的程序也只选拔三次按钮通告,看来大家的指标就像达到了图片 4
。但在平凡玩游戏中,我们同临时候按下的键不单单独有三个,例如边走边砍杀敌人,就有十分大可能率按住右方向键不放,然后使劲的按A或B键,那那样的话又会情不自禁哪些的情状呢?那样的话,在大家的“轮循”中就有望现身以下的情景(“->”表示前后相继顺序):

收获当前按下的是“右方向键”(1) –> 取妥贴前按下的是“右方向键”(2) –>
取安妥前按下的是“右方向键”与A键(3) –> 取妥当前按下的是“右方向键”(4)
–> 取妥帖前按下的是“右方向键”与B键(5)–>
取伏贴前按下的是“右方向键”(6) ……

在下面中,(1)与(2)可经过上边的消除办法归拢为三回,但到第3步时,因为脚下按下的键有多少个,而前贰遍按下的开关唯有二个,所以因(2)开关的两样,又再一次发出一遍开关文告。如此类推,从(1)到(6)步,程序就觉着“右方向键”共按了5次!但对此大家“人”来说,那不是大家想要的结果,因为大家只是平昔按住“右方向键”不放,所以应该只算按二次。那看来上边的化解方式并不到家图片 5

让我们再细致再看一下地点的非常流程中的(2)与(3)中的差距,明眼的您应该看出来了,它们中间只是多了二个A键。而后生可畏旦“右方向键”在第一步时已发出了按钮新闻通告,那么在(3)步时,借使我们只产生“A键”的按钮新闻通告,也就说每一次只产生此番按下的开关集结与上一回按下的开关集结的差的按钮新闻文告的话,那么在上头的流水线中,发出的音信布告就唯有:在(1)步时发出“右方向键”的按钮布告、(3)步时发出A键的按钮文告、(5)步时发出B键的开关布告。这样篇头中的难题就能够圆满的缓慢解决了图片 6
!!

(轨范代码可参看源码中OnTimerCallback函数卡塔 尔(英语:State of Qatar)

 

到此,“C#对游乐手柄的编制程序开荒”的稿子就解说完了,下大器晚成篇大家会讲课一下怎么去实现率先篇中说的“用游戏手柄模拟键盘或鼠标”的软件图片 7
。很简来讲之,风野趣的相恋的人期望能回贴扶持一下自己图片 8

 

源码下载:
/Files/kingthy/JoyKeys.Voluntary.rar

固然Flash中不扶助对娱乐手柄进行编制程序,但大家能够换种方法,做叁个扶植程序(外挂?图片 9
),将手柄中的操作事件转变为Flash中可担负的键盘与鼠标操作事件,那样不就能够使用游戏手柄来玩Flash游戏了呢?!于是,上网查了相关资料,但却发掘唯有C++方面的案例,而C#叁个也找不,那不打紧,本人入手,男耕女织图片 10

 

 

(注:肖似那样的职能,互连网已有现存的软件,是三个马来西亚人付出的,叫乔伊ToKey)

 

对娱乐手柄举行操作,大约有几种情势:接收系统API也许利用DirectInput操作游戏手柄设备。(恐怕还会有其余方式,但作者的学问范围有限,别的方法就全无所闻了卡塔 尔(阿拉伯语:قطر‎

选拔系统API是风流罗曼蒂克种最简单易行的法门,因为系统已帮大家封装好了具备细节,我们只要在先后中按期拿到游戏手柄设备的情景就能够了(轮循卡塔 尔(阿拉伯语:قطر‎。

 

操作游戏手柄(杆卡塔尔国的API有以下多少个

函数名称 函数说明
joyGetNumDevs 获取当前系统支持的游戏设备数量
joyGetDevCaps 查询获取指定的游戏杆设备以确定其性能
joySetCapture 向系统申请捕获某个游戏设备并定时将该设备的状态值通过消息发送到某个窗口
joyReleaseCapture 释放对某个游戏设备的捕获
joyGetPos 获取游戏设备的坐标位置和按钮状态
joyGetPosEx 获取游戏设备的坐标位置和按钮状态
joyGetThreshold 查询指定的游戏杆设备的当前移动阈值
joySetThreshold 设置指定的游戏杆设备的移动阈值

 

其间,依据调用差别的主意又可分为三种形式。

1卡塔 尔(阿拉伯语:قطر‎被动形式:

    
调用joySetCapture方法,向系统报名对有些游戏手柄的捕捉,假使成功申请,系统将会定时将此游玩手柄的图景音信通过新闻方式布告到大家的有些窗口上。

2卡塔 尔(阿拉伯语:قطر‎主动情势:

    
便是依据大家温馨的须要,按需调用joyGetPos或joyGetPosEx方法查询获得某些游戏手柄的当前状态。

 

而在本篇中,大家要上课的只是“被动方式”。

 

joySetCapture方法的C#概念原型如下:

        /// <summary>
        /// 向系统申请捕获某个游戏杆并定时将该设备的状态值通过消息发送到某个窗口
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="uJoyID">指定游戏杆(0-15),它可以是JOYSTICKID1或JOYSTICKID2</param>
        /// <param name="uPeriod">每隔给定的轮询间隔就给应用程序发送有关游戏杆的信息。这个参数是以毫妙为单位的轮询频率。</param>
        /// <param name="fChanged">是否允许程序当操纵杆移动一定的距离后才接受消息</param>
        /// <returns></returns>
        [DllImport("winmm.dll")]
        public static extern int joySetCapture(IntPtr hWnd, int uJoyID, int uPeriod, bool fChanged);

当我们调用此情势向系统报名捕获有个别游戏手柄后,即便成功,则赶回JOYE途观ENCORE_NOE索罗德RO大切诺基(值为0),不然重返其余值的话代表报名停业。况兼在不再要求捕获游戏手柄时要记得调用joyReleaseCapture方法释放捕捉。

 

即使申请成功,系统将会准时(依据uPeriod的值决按期期的长度卡塔尔国将游戏手柄的事态以音讯包方式发送到hWnd对应的窗口分界面。所以大家必供给在程序中拍卖相应的音信(如重写WndProc方法开展管理卡塔尔。

同一时间依据分化的uJoyID值,系统一发布送的新闻号又会有所差异,如对于JOYSTICKID1连串将会分别发送以下消息包:

消息号 说明
MM_JOY1MOVE 当手柄的位置已变动或按了某些按钮时,将会发送此消息包。
MM_JOY1BUTTONDOWN 当手柄的A,B,C,D四个按钮中的一个或多个正被按下时,将会发送此消息包。
MM_JOY1BUTTONUP 当手柄的A,B,C,D四个按钮中的一个或多个正被弹起时,将会发送此消息包。

 

而对此JOYSTICKID2
系统一发布出的新闻包分别为MM_JOY2MOVE、MM_JOY2BUTTONDOWN、MM_JOY2BUTTONUP!

并且要注意!不管您有未有按游戏手柄上的开关,系统也会准时发送MM_JOYXMOVE消息!!

 

什么样决断按了怎么着键?

在音讯包中,游戏手柄的意况音讯(开关状态卡塔尔分别存储在音信包中的WParam与LParam参数。

1)WParam参数:

对此游戏手柄来讲WParam存储的是了上下左右三个方向键之外的富有开关中当前被按下的开关值,它的值是二个复合值。如它的值为JOY_BUTTON1
| JOY_BUTTON2时,就标识按下的按钮是1号和2号开关。

注意:对于MM_JOYXBUTTONDOWN与MM_JOYXBUTTONUP八个音信,用于剖断的按键值是不相同于MM_JOYXMOVE的按键值!!

 

2)LParam参数:

此参数存储的是17日游手柄的坐标参数,而且此参数的高拾伍位存款和储蓄的是Y坐标值,低十四人存款和储蓄的是X坐标值。

而对此游戏手柄来说,剖断上下左右五个倾向键有未有被按下正是由此此参数进行判断的。尽管当四个方向键都不曾被按下时,表示近来游乐手柄处于主旨坐标中!也正是X,Y坐标都是在着力点地点上,而当一些方向键被按下时,X,Y坐标将基于所按的键向对应方向偏移。如当按了向右键,则X坐标向右偏移,Y坐标保持在大旨点地方,而只要按了右、上多个方向键同有时候按下,则X坐标向右偏移,Y坐标向上偏移。所以我们能够根据LParam参数得到X,Y坐标的值,然后再依靠在这之中央点来推断。仿效代码如下:

       /// <summary>
        /// 获取X,Y轴的状态
        /// </summary>
        /// <param name="lParam"></param>
        /// <param name="buttons"></param>
        private void GetXYButtonsStateFromLParam(int lParam, ref JoystickButtons buttons)
        {
            //处理X,Y轴
            int x = lParam & 0x0000FFFF;                //低16位存储X轴坐标
            int y = (int)((lParam & 0xFFFF0000) >> 16); //高16位存储Y轴坐标(不直接移位是为避免0xFFFFFF时的情况)
            int m = 0x7EFF;                             //中心点的值
            if (x > m)
            {
                buttons |= JoystickButtons.Right;
            }
            else if (x < m)
            {
                buttons |= JoystickButtons.Left;
            }
            if (y > m)
            {
                buttons |= JoystickButtons.Down;
            }
            else if (y < m)
            {
                buttons |= JoystickButtons.UP;
            }
        }

 

好了,对游乐手柄的“被动格局”编制程序就讲明完结了,剩下的就是要怎么使用游玩手柄来兑现模拟键盘或鼠标的操作了……

PS:假如各位有意思味的可思虑一下怎么贯彻游戏手柄的“主动情势”编制程序开采。

 

演示代码下载:

/Files/kingthy/JoyKeys.rar

相关文章