InputManager Update Frequency

InputManager Update Frequency 

 仅供个人学习使用,请勿转载,勿用于任何商业用途。

 

    很高兴上一篇文章引起了很多争论,主要是关于以什么频率更新InputManager。大概是上一篇文章说的不够清楚,所以我想再说的详细一点。

    首先,为什么需要IM。Xna只提供了最基本的输入检测方式:只能主动查询某个按键是否被按下,没有事件支持,没有常用的keyJustDown和keyJustUp。其次,我希望在一个地方处理键盘事件,然后转换为响应的系统消息传递给上层组件,这样便于管理,可以非常方便的让用户自定义按键,并且避免冲突。直接让基本的IsKeyDown这样的函数遍布在代码的各处,会很难修改代码,你甚至无法知道哪个对象在捕获输入,捕获了哪些输入。对于同时捕获了相同输入的对象(比如两个textbox),会出现一连串副作用,当然,我们可以让每个对象判断自己当前是否获得了焦点,不过这样会导致大量重复代码,并且更难维护。最后,我希望通过IM,提供类似winForm或者WPF的输入处理方式。
 
    接下来,为什么要以固定频率更新IM。为了方便讨论,这里仅仅以xna的Game框架为例:
while() { Update(); Draw(); } 
     以上是基本框架的简化,只要游戏运行以后,就将不停的顺序调用Update和Draw,当然,当xna中的isRunningSlow标志被设置时,会暂时忽略Draw,不过这里我们只假设Update和Draw是一一对应的。

     前面所指的“以固定频率更新IM”表示以某个确定的值更新IM,而不是每次Update都更新。这里指的更新是:判断是否需要触发某个事件(keyboradAction,mouseAction),以及按键的状态是否为Pressing。下图是IM的接口:
InputManager Update Frequency_第1张图片
     InputState将作为输入事件的参数传递给事件订阅者。其中,有2个事件是非常特别的pressed, moving,因为这两个事件是持续性的,而程序更新的时间是离散,很显然的问题就是以什么频率触发这2个事件。当然,最简单的方式就是每次update只要检测到这两个状态就触发事件。但这样并不是最高效的方法。如果游戏每秒调用数百次Update,就意味着数百次事件触发。有人觉得这样的调用是微不足道的,那么先设想一下你可能如何编写代码:
KeyboradAction += MoveCharacter; MoveCharacter() { if( keyState == pressed && keyCode == w) moveForward(); } 

 相信任何人都编写过以上代码。稍微分析以下这段代码可能引起的操作,角色位置改变,意味着坐标变换,矩阵乘法。如果他在sceneGraph中有子node,那么将是一系列矩阵乘法。此外,你需要碰撞检测,检查是否可以移动。动态物体的位置改变,还有可能导致重建sceneGraph,接下来周围所有对象要对你进行AI反馈,等等。想想,每秒数百次,你还觉得代价微不足道吗?在textbox中,按下了A键1秒,就显示出上百个相同的字符,你不觉得bug吗?当然,可以让高层逻辑检查满足一定的条件在作出相应,但这就意味着检查代码会遍布所有对象,所有对象都需要对每一次相应作检查,所有使用IM的人都必需要知道可能发生的潜在问题。为什么不在底层就解决这个问题呢?更何况根本就不需要那么精确的输入精度,即使对于实时交互式程序来说,每秒30~60次更新就足以满足绝大部分需求。

     因此,在我的设计中,只会每隔固定的一段时间,检查是否需要触发某个事件,而不是随Update的速率而改变,保证无论update的频率如何,我的输入更新频率总是一定。当然,这里的IM的刷新频率只能小于等于Update的频率。

     希望以上的解释足够清楚。此外,我在之前的文章中也说过,如果你一开始就考虑程序以固定每秒30或者60次的速度更新,那么就可以完全不用考虑这个问题。最后,在我的实现中,这些而外的输入控制代码只用了几个简单的判断语句而已,我实在没有理由不这么做:
if( updateCount >= updateInterval) { check whether we should fire any event; updateICount= 0; } else updateCount += someValue;

 

以下是我对一些问题的回答:

“不理解按键事件是否这么必要另外你保留了 IsKeyDown的方法这方法和你的事件一起使用不容易乱么”
在上一篇的第二点我说过所有输入都应该通过事件获得。之所以保留了IsxxxPressing这样的函数,因为这是某个框架中的一部分,我希望让使用者获得尽可能多的控制权,同时,这2个函数并不会引起任何逻辑上的错误。

“键盘打字速度的世界记录是大约每分钟800次,也就是每秒13次。很遗憾 据我所知 高桥名人的记录是1秒16次 30次的刷新小小不够“
800次是我google到的结果,可能不一定正确吧:) 之所以提供这个值,是让大家在设计自己的程序时作为一个参考。合理的IM采样间隔就是在不遗漏任何输入情况下的某个最小值。有人觉得推敲用户的输入速度没有必要,假设不知道输入速度,由如何能找出合理的采样间隔呢? 其实我觉得不论13次也好,16次也好,每秒30次的刷新已经足够,如果感兴趣的话,可以自己做个测试看看。13次或者16次,只是非常非常罕见的瞬时状态,这时就算丢失1,2次输入也并不是什么问题,更何况没有游戏需要那么快的输入频率,就像wow每个技能都有冷却时间,冷却时间之内,按的再快也没用。

“效率看起来微乎其微 和一个会放上万Sprite的游戏比起来 优化放在更核心的地方比较好效率“
上面已经讨论过,过高的刷新率会引起一连串的连锁反映,看似微不足道,实际却会被放大上百倍。如果用户知道某个事件将以什么频率触发,那么他编写起程序来也更容易,不是吗?系统的效率取决于系统中的短板,对于游戏更是如此,我希望让每个部份都尽可能的高效。

“那样做出来的东西就是 理论刷新率上百 而地图1秒实际的移动次数只有IM的30 那反而是浪费了上百的刷新率“
理论上,60fps以上人眼就看不出区别了,这也就是为什么很多游戏锁在60fps。不过有人就是喜欢炫耀自己的fps,那就让他浪费好了, 但我们要保证在速度不同的两台机器上,程序行为是一样的。我们是在做游戏而不是精确的物理仿真程序,30~60的更新已经足以:)

 

 

 

你可能感兴趣的:(游戏,框架,WPF,WinForm,IM,textbox)