A Simple Input Manager for XNA

A Simple Input Manager for XNA

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

(update on 4.28 09: 这篇文章的设计是有bug的,详细更新可以看这里)

           Why we need input manager? 在PC平台上,KeyboardState和MouseState提供了基本的用户输入信息,但是对实际编写游戏来说,还缺少了几个关键功能。首先,这两个类只能“拉”出数据,而不能主动“推”数据,也就是说,需要客户端显式查询某个键的状态,而不能当键状态发生改变时通知客户。其次,IsKeyDown和IsKeyUp返回的值并不是我们所预期的。对于普通的KeyDown事件来说,只在按键第一次被按下的时候触发,比如我们在t1时刻按下按键,触发keydown事件,到t2时刻,即使按键仍然处于按下的状态,也不应该触发keydown事件。而对于IsKeyDown来说,不论何时,只要按键处于按下状态就一直返回true,IsKeyUp也具有同样的行为。我习惯把XNA的IsKeyDown成为pressing,而把只在按键第一次状态变化时触发的事件成为keydown或者keyup。最后,查询某个键被按下了多久,也是很常见的操作。

           基于以上讨论,input manager其实只是一个简单的类,只需要具有以下功能:
1. 同时支持推和拉模型
2. 支持以下事件:KeyDown, KeyUp, KeyPressing, MouseDown, MouseUp, MousePressing, MouseMove, MouseWheel。大部分事件接受一个void GenericEventHandle<T>(T t); 类型的委托
3. 支持查询某个按键被按下的时间

 我们可以马上写出Input Manger的接口:

           实现起来也非常简单,保存当前和上一次更新时的键盘,鼠标状态。游戏主循环调用Update,每次更新时次迭代所有键,检查状态是否有改变,触发相应的事件。为了支持时间查询,用一个Dirctionay来记录每个按键被按下的时间。

          这里有两点需要注意的地方:首先,迭代更新按键状态不要用foreach + Enum.GetValues(typeof(Keys))的方法来实现。GetValues基于反射,相对来说是比较慢的操作。可以尝试把所有枚举值保存到普通数组里,迭代数组来遍历所有按键要快的多。另外,记录每个按键时间的Dirctionay不要用Enum做key。使用Enum类型做key,需要进行装箱。把Enum转换成int再做为Dirctionay的键值来使用,就能得到很大的性能提升。

          当然这里还有一个潜在的问题,应该以多快的频率来更新输入状态呢? 前面提到过,在游戏主循环中调用update(),这意味着对于IsFixedTimeStep为true的XNA游戏来说,将每秒更新60次;而对IsFixedTimeStep为false的游戏来说,根据实际情况,每秒更新几十次或者几百次都是有可能的。问题是我们需要每秒更新那么多次吗?目前键盘打字速度的世界记录是大约每分钟800次,也就是每秒13次,我没有找到鼠标点击速度的世界记录,不过自己测试了以下,每秒6~8次点击已经是极限速度。根据奈奎斯特(Nyquist)采样定理,只要采样频率大于信号中最高频率的2倍时,就能完整保留原始信号中的信息,也就是说,即使你是世界冠军,每秒30次更新就足以捕获你的所有输入。从上面的类图中可以看到,我们允许用户分别设置键盘,鼠标击键的更新频率和鼠标移动事件的触发频率,因此,update中的代码可能看起来是这样的:

  1. public void Update(GameTime gameTime)
  2. {
  3.     float elapsedTime = (float)(gameTime.ElapsedGameTime.TotalSeconds);
  4.     keyboardUpdateCount += elapsedTime;
  5.     mouseButtonUpdateCount += elapsedTime;
  6.     mouseMoveUpdateCount += elapsedTime;
  7.     if (keyboardUpdateCount >= keyboardUpdateInterval)
  8.     {
  9.         //Update keyboard state,fire keyboard event
  10.         keyboardUpdateCount = 0;
  11.     }
  12.     if (mouseButtonUpdateCount >= mouseButtonUpdateInterval)
  13.     {
  14.         //update mouse state,fire mouse event
  15.         mouseButtonUpdateCount = 0;
  16.     }
  17.     if (mouseMoveUpdateCount >= mouseMoveUpdateInterval)
  18.     {
  19.         //update mouse position,fire mouse move event if needed
  20.         mouseMoveUpdateCount = 0;
  21.     }
  22. }

           你可以注意到Input Manager还有一个active属性。原因是当XNA游戏失去焦点时,KeyboardState和MouseState仍然能捕获到用户输入,对大多数游戏来数,这显然是不必要的,所以当游戏失去焦点时,我们将把Input Manager的active属性设置为false,停止更新输入。

接下来的工作
         

 

 最后要说的是,KeyboardState返回的所有键盘消息,都是Keys枚举的一个值,如果你想编写文本框,允许用户输入,必须把各个枚举值转换为相应的字符。详细的实现也许非常复杂,你要考虑shift按下或者Caps Lock是否开启; 又或者你希望把每个键盘消息映射为游戏内的某个消息,等等。那么也许就需要在input manage之上,再建立一个InputMessageTranslator之类的东西了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值