[教程] [LiiFTG](二)实用级键盘检测类
本帖最后由 Aone 于 2009-2-10 15:03 编辑
这几天把FTG用的键盘检测类重新优化了一下,event类写的稍微有点乱。
特此放上所有类包和测试文件。基本实现单/多键的up down hold事件的检测,可返回hold时间以及void时间,可设置事件触发频率。并且解决了连续检测的不规则间隔问题。另外可能会比普通的键盘检测占用资源多一些。
心得会逐步补上,先上代码。设置了点阅读权限,请谅解。 testKey.swf (8.67 KB)
LiiFTG_key.rar (24.01 KB)
第二节 键盘信息获取以及初步处理
在上一节中我们知道如何利用键盘侦听来获取各种键盘输入的状态。那么这一节我们来讲一下如何正确获取键盘输入的所有信息。格斗游戏的手感一方面来自于游戏对玩家输入的反应速度,一方面来自输入和反馈的准确性,反应速度越快约准确手感越好。反应慢或者不准确的反馈手感将会很僵硬。
首先,我们了解一下一套控制系统的按键组成。
1)方向键(十字键/摇杆)
可以说方向键是搓招系统的主要部分,虽然只有4个键但是从中组合出来的各种按键顺序带来千变万化的招数。一个招数的输入起始基本都是由方向键开始。特点是最多两键组合。不会出现两键以上的组合(十字键和摇杆由于物理特性无法做出… 键盘倒是可以随心所欲的组合)。
2)攻击键
配合方向键决定最终攻击方式的存在,可以看做是一个招数的输入终结点。相对于方向键攻击键可以设定任意组合方式(只要按的过来…)。
3)系统键
投币、开始游戏、暂停等等。
其次,每个键的信息和属性构成(以键盘为例)。
1)按键名称
给按键一个名字,比如“上”“下”“左”“右”“A”“轻拳”之类的。
2)按键对应的键盘代码
在侦听时所对应的keyCode
3)按键状态
当前这个键的状态是按下还是抬起
4)按键类型
是方向键还是攻击键还是系统键
5)按下时间
按下后到抬起前的时间
6)抬起时间
抬起后到按下前的时间
7)其他用于计算的属性
比如这个按键是否属于组合键中的一个,如果是的话就有按键组合的名称和ID等属性需要设置。
8)按键可以注册各种事件,比如按下,抬起,按住3种事件。
为了实现事件触发就要继承并实现IEventDispatcher接口。
在了解了一个按键所需要的属性和信息我们就可以开始着手制作一个FTG用的key类或者一个接口和一系列key类。在这里我选择了接口,因为FLASH中除了键盘外还有鼠标的几个按键。当然我主要讲键盘监听,所以鼠标的那个有兴趣的可以去扩充。
接口代码如下:
- package Lii.FTG.Key{
- import flash.events.IEventDispatcher;
- public interface LiiKey extends IEventDispatcher{
- //
- function get keyName():String
- function set keyName(Name:String):void
- //按键名称可随便起
- function get charCode():String
- function set charCode(code:String):void
- //按键的键盘字母或者鼠标键名
- function get keyCode():uint
- function set keyCode(code:uint):void
- //按键代码
- function get holdTime():uint
- function set holdTime(time:uint):void
- //按下时间
- function get voidTime():uint
- function set voidTime(time:uint):void
- //抬起时间/空闲时间
- function get combined():Boolean
- function set combined(type:Boolean):void
- //是否是组合键
- function get type():String
- function set type(type:String): void
- //类型
- function get disable():Boolean
- function set disable(boolean:Boolean):void
- //是否可用
- function get groupName():String
- function set groupName(Name:String):void
- function get groupID():int
- function set groupID(ID:int):void
- //对应的按键组名称和ID
- function isDown(setState:Boolean=false):Boolean
- function isUp(setState:Boolean=false):Boolean
- //当前是否是down或者当前是否up
- }
- }
接下来讲一下按键信息获取的阶段的几个重点和心得。
一、提高组合匹配效率。
用KeyboardEvent.KEY_DOWN和KeyboardEvent.KEY_UP来改变一个按键组对应的数组值然后判断组合的方法我在第一节中已经讲解过并且有实例了。在改良过程中发现利用数组的join()方法并不是最快速的,相比之下把数组转换成数字要快很多,而且数字之间匹配的效率也要比字符串之间匹配要来的快。于是在新的keyCatch类中改用全数字判断以提升效率。
这里要注意的是把数组转换成单个数字的过程,一般可能都会想到用join()之后parseInt()来转换,但是这样做的话还是用到了join()的前提下又多用了parseInt(),效率反而会降低。我们可以用for循环累加的方式来获得这个数字。
代码如下:
- function _getCodeInt(target:Array):int{
- var outI:int=0
- target.forEach(callback)
- function callback(item:*, index:int, array:Array):void{
- item&&(outI+=Math.pow(2,index))
- }
- return outI
- }
二、事件分离
让单纯的KeyboardEvent.KEY_DOWN分离成KEY_DOWN和KEY_HOLD 。
我们都知道Flash的键盘侦听在第一次KEY _DOWN被触发之后如果按住的键没有松开就会以一定频率连续触发KEY _DOWN事件。这个事件触发的间隔不可控,并且存在一个BUG。就是在第一次按下后会有一个比较大的停顿,第2次之后的间隔要比第1次与第2次之间的间隔小很多。普通应用上这个很无所谓,但是对于格斗游戏来说这个直接严重影响了轻击和双击的判断。
由于我们要获取按下的时间,我们可以给每个按键设置一个holdTime属性,当第一次KEY _DOWN触发后以固定的频率让holdTime递增,而当KEY _UP触发时再把holdTime归0即可。那么当第1次KEY _DOWN时holdTime应该是1其余时候不是0就是比1大,那么我们可以根据holdTime的值区分事件1时为KEY _DOWN大于1时为KEY _HOLD。至于KEY _UP还是交给KeyboardEvent. KEY _UP去触发。
三、解决连续触发间隔不同的BUG
前面一段中我们提到了那个BUG,对于这个BUG在很多游戏制作者中比较流行的解决办法是Key.isDown()配合onEnterFrame或者侦听配合onEnterFrame来解决。这是个比较有效的方法,这里我把它少许的改良了一下用侦听配合setInterval。可能有人会说Flash的setInterval并不精准,特别是在间隔很小的情况下出入很大。其实是可以通过某些手段认为的让其准确到1毫秒级的,在这里我卖个关子等以后讲到FTG动画的时候再说这个东西。那么用setInterval比onEnterFrame到底好在哪呢,那就是可以动态的改变检测的时间间隔而且setInterval本身的最高发生频率也要比onEnterFrame高可以在动画帧频不高的情况下保证函数运行密度。细心的玩家可能会在我开源的AS2.0格斗引擎中看到游戏是以最高20fps的速度运行的但是检测精度和动画播放却明显比20fps高并且ie和flashplayer的播放速度差别很小,这都是拜setInterval所伺。
那么应当如何让侦听和setInterval协同工作呢?
我们可以让侦听只去改变Key的holdTime/voidTime的值,而不触发对外的任何事件。让setInterval的函数检测到holdTime/voidTime的变化符合了要求后再触发KEY _DOWN和KEY _HOLD事件。也就是把原先的KeyboardEvent.KEY_DOWN经过一次包装后以新的FTGKeyEvent.KEY_DOWN和FTGKeyEvent.KEY_HOLD来冒泡。检测之余setInterval的函数也在无时不刻的以一定频率更新着holdTime/voidTime的值。
以上的具体代码都在Lii.FTG.Key.KeyCatch类中可以找到。