如果也能利用手柄来玩Flash游戏,在此办法下

前段时间花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结构体所占据的内存空间大小(其值可透过马尔斯hal.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的值。判断格局在上一章中有讲,那里就不再细说,或者也足以看后面提供的源码。

 

因为“主动格局”的“时效性”只有四回,所以为了能够时刻监视到游戏手柄的按键事件,就非得开展“轮循”获取,当监视到娱乐手柄有按键爆发时就进行事件通报(噫?好像“被动形式”?嗯,其实当我们向系统报名捕捉某个游戏手柄时,系统末段也是在帮大家开展“轮循”操作!)。而落成“轮循”的措施则足以有多样措施,比如利用单独的线程举行一个死循环;或者选择提姆er进行定时执行。

 

但当我们的操作进入“轮循”后,如若也是直接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

 

 

(注:类似这样的效益,网络已有现成的软件,是一个日本人付出的,叫乔伊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);

当大家调用此办法向系统报名捕获某个游戏手柄后,倘使成功,则赶回JOYERR_NOERROR(值为0),否则再次来到其余值的话代表申请破产。并且在不再须求捕获游戏手柄时要记得调用joyReleaseCapture方法释放捕捉。

 

要是申请成功,系统将会定时(根据uPeriod的值决定时间的尺寸)将游乐手柄的景况以音信包格局发送到hWnd对应的窗口界面。所以大家亟须求在先后中处理相应的消息(如重写WndProc方法举行拍卖)。

与此同时按照分裂的u乔伊ID值,系统发送的新闻号又会有所不一样,如对于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

相关文章