这么不纵可应用游戏手柄来玩Flash游戏了邪。对戏手柄的编程开发-API篇(1)

前段时间花38首位从网上购了平针对北通的USB游戏手柄,这样周末跟夜晚之闲雅时间虽可玩玩孩儿时的SFC与街机模拟游戏了。图片 1

追忆“被动方式”开发

在C#针对戏手柄的编程开发-API篇(1)立首文章中我们介绍了“被动方式”的开支。在斯办法下,我们的主次只去一个音讯接收者。系统会定时告诉我们有游戏手柄当前的状态,我们的次接收至晚更依实际上得进行处理即可。但一旦你是一个缜密之总人口,你会发觉如直接以信息事件处理的言辞会在一个问题,如我辈循下某个键(比如提高的方向键)然后推广时,对于我们“人”来说,我们本下及弹起的立有限只动作应只是说明我们一味点击本条按钮一坏。但对网吧,它仅是形而上学地定时通我们的顺序于某个时间内游戏手柄的顺序按钮的状态,而以咱们据下及弹起这段时间内,系统来或已传递了N次的消息通知(N值根据捕捉时设置的uPeriod值与汝的按键速度来控制),通知手柄有按钮处于被准下状态,而如我们即便依据消息包直接处理点击事件的言辞,就会造成问题出现(比如在某游戏被,我们筹的凡当点击一不好手柄的右键,就用角色上走一步。但由我们仍下按钮到弹开此按钮这段时,由于人数之反应速度远远慢吃计算机的处理速度,所以这段老不够的工夫外,系统可能曾经通知了10糟以上之信息包表明游戏手柄右键已受准下,这就算导致我们随平不行右键,游戏被的角色却出或早已倒了十步的多,这可是免是咱们怀念使的结果)。那咱们若什么处理此“点击”事件才好避免重复通知为?这便是本篇最后只要着重讲解的内容了……

以上课这个题材之解决方式之前我们还来讲课一下上文还涉嫌的平种植开发方式。

 

 

“主动方式”的支付

主动方式尽管我们不欲为网报名注册捕捉某个游戏手柄,我们只是冲自己之要按时去取游戏手柄的状态信息

这时我们且采取以下的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结构体所占有的内存空间大小(其值可经Marshal.SizeOf求得)。而要假定取游戏装备的任何参数,则还必须使安装dwFlags参数的价值!否则只能获得坐标值(dwXPos)。如对戏手柄来说我们需要得到其它按钮的状态,则设置dwFlags的价值吗JOY_RETURNBUTTONS,用于指示我们用返回所有按钮的状态。

示范代码:

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函数获取手柄状态数据成功,则回JOYERR_NOERROR(值为0),否则回其它值的语代表收获失败。

当数码获得成功后,对应之一日游手柄的状态数据还已经囤积于JOYINFOEX结构实例中了。如要一口咬定是否仍下了方向键,则只是看清dwXPos与dwYPos的值;而判断是否比照了别样按钮,则只是看清dwButtons的价值。判断方式以达成同一段中出言,这里就是不再细说,或者也得以关押后边提供的源码。

 

以“主动方式”的“时效性”只生雷同软,所以为了能够时刻监视及娱乐手柄的按键事件,就非得开展“轮循”获取,当监视到打手柄有按键发生常即便开展事件通报(噫?好像“被动方式”?嗯,其实当我们往网报名捕捉某个游戏手柄时,系统最终吧是于扶助咱开展“轮循”操作!)。而落实“轮循”的艺术虽然可以产生强法,比如使独立的线程进行一个死循环;或者用Timer进行定时执行。

 

然当我们的操作上“轮循”后,如果为是直接joyGetPostEx就处理的话语也同等会逢篇头所说之深不好问题图片 2
!因为任是“主动方式”还是“被动方式”都是如出一辙只能得到游戏手柄按钮时之状态(按下要未按照下)。那怎么解决吧?

 

某日在有网站及打闹一个Flash游戏时,突然想到,如果也会应用手柄来玩Flash游戏,那该多爽图片 3
。但可惜的是,目前底Flash都是未支持针对戏手柄进行编程,这难免是Flash中的一个不满。。

釜底抽薪按钮重复状态的题目

釜底抽薪是题目,如果理清矣思路,其实为是老大粗略的措施。

咱俩透过API得到的是打手柄按钮时之状态(被以下还是无依下)。因此我们可以在“轮循”里,每当监视及戏手柄在某次时间来某些按钮是地处“按下”状态时,就记下此次给准下的按钮号,这样就一破“轮循”操作时,如果也监视到起按钮按下,则通过和上一致不好仍下之按钮对比,如果还是同的按钮,则表明此次按钮还是继续上次的准下状态,那就不再要为程序里生信息通知了。而设非同等,则闹新的按钮按键通知,并记下本次按下的按钮号。

伪代码如下:

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函数)

 

到此,“C#对娱乐手柄的编程开发”的稿子就讲解结束了,下一致篇我们会讲课一下庸去贯彻率先篇中说的“用娱乐手柄模拟键盘或鼠标”的软件图片 7
。很简单的说,有趣味之意中人想会回贴支持一下我图片 8

 

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

虽然Flash中莫支持对戏手柄进行编程,但咱好变种艺术,做一个救助程序(外挂?图片 9
),将手柄中之操作事件转换为Flash中而领之键盘和鼠标操作事件,这样非就可使游戏手柄来玩Flash游戏了邪?!于是,上网查阅了有关材料,但也发现只有C++方面的案例,而C#一个啊找不,这不自困难,自己下手,丰衣足食图片 10

 

 

(注:类似这样的功效,网络已产生成的软件,是一个日本口开之,叫JoyToKey)

 

针对娱乐手柄进行操作,大概发生半点栽办法:采用系统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);

当我们调用此道为网报名捕获某个游戏手柄后,如果成功,则赶回JOYERR_NOERROR(值为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参数:

以此参数存储的凡娱手柄的坐标参数,并且是参数的赛16各类存储的是Y坐标值,低16个存储的是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

相关文章