事件队列在游戏开发中的应用

        事件队列是一个按照先进先出顺序存储一系列通知或请求的队列。发出通知时系统会将该请求置入队列并随即返回,需要进行处理对象随后从事件队列中获取并处理这些请求。请求可由处理者直接处理或转交给对其感兴趣的模块。这种方式对消息或事件的发送者与受理者进行了时间上的解耦,使消息的处理变得动态且非实时。

        如果你曾从事过用户界面编程比如Windows图形界面相关的事件驱动式编程,那你肯定对“事件”不陌生了。每当用户与你的程序交互时:比如点击按钮,下拉菜单,或者按下一个键盘的键,操作系统都会为之生成一个事件。每到来一个事件系统将这个事件对象放到一个队列中,然后立即返回去处理其他任务,你开发的应用的任务就是从队列中获取到感兴趣的事件并将其与你自定义的行为挂钩。这个过程如下图所示:

         我们在开发网络游戏时,有时会在短时间内收到大量的网络事件,此时如果我们都在一帧处理的话就是造成游戏的卡顿。这时我们可以使用事件队列,每来一条网络消息,先不处理,把它放到一个队列里面,然后在游戏的帧循环里面根据游戏的需要分帧取出网络事件进行处理。整个过程类似上图,只是事件生产者来自于网络罢了。

        我们在游戏开发中一般都会对声音进行预加载,声音文件如果很大的,在游戏进程中加载的话,这样的IO过程虽然会短时间阻塞进程,但是对于我们游戏进程的流畅性而言是很大的伤害。在《Game Programming Patterns》中就举了一个声音播放的例子。作者就是利用消息队列,在客户程序调用播放声音接口时不时立即加载音效进行播放,而是产生一个事件放入事件队列中,然后在合适的地方实现一个处理程序来加载音效并播放声音。这样做的好处是可以避免直接播放音效时由于加载过程阻塞游戏进程(直观感受就是游戏卡顿了)。而且我们可以遍历事件队列把相隔时间很近的事件合并处理,这样可以解决短时间内对于多个音效的播放请求处理会造成声音重叠放大的问题。如果你所使用的游戏引擎支持多线程,那么你也可以单独开一个线程,读取事件队列进行处理,不过要处理好事件的同步问题。

        在使用事件队列时要尽量避免在处理事件端代码中发送事件,因为这样有可能会造成死循环导致栈溢出并造成游戏崩溃。就像下面这样:

1. A发送一个事件。

2. B接收它,之后发送一个响应事件。

3. 这个响应事件恰巧是A关心的,所以接收它。作为反馈A也会发送一个响应事件……

4. 回到2。

        一般我们实现一个队列使用一个数组就够了。使用数组还有一个好处就是我们对队列首尾下标向右移动时进行下标相对数组长度的取模运算,可以实现一个基于环状缓冲区的队列。这样的队列对内存的利用更高效,它没有动态内存分配,不需要向周围拷贝内存。

        在很多方面,事件队列可以看做是观察者模式的异步版本。如果你对Go语言内置的"通道"类型了解的话,你会发现它本质上就是一个事件队列或消息队列。

参考:Event Queue · Decoupling Patterns · Game Programming Patterns

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值