游戏编程模式之事件队列模式

对消息或事件的发送与受理进行时间上的解耦。

(摘自《游戏编程模式》)

  事件队列模式维护着一个事件队列。元命令入队意味着我们主观想要立即想要执行该命令的相关操作;而出队意味着响应执行该命令相关的操作。受限于各种硬件和软件的情况,这些命令执行并不是立即响应的,但顺序是固定的、也不会出现命令遗漏的情况。我们使用事件队列模式,很重要的目的就是序列唯一且不会遗漏。这一模式适合管理对实时要求不算高的命令响应。

  和这以模式类似的有:观察者模式和命令模式。但是他们之间的思考方向和解决的问题是不一样的。观察者模式、命令模式是对发送事件方和接受(执行)事件方的解耦;事件队列模式是某一个问题上对时间(执行顺序严格但不要求严格实时)进行解耦。

  既然都画出了自己理解的三个模式的图示,那么还是有必要介绍一下我对它们的理解。

  • 命令模式。在命令模式中,一个命令绑定的这一个操作。在执行命令时需要传入执行对象,针对不同的执行对象执行相同的操作。
  • 观察者模式。在观察者模式中,一个命令绑定着或多个操作(也可以按照观察者模式的说法:一个或多个操作订阅某一个命令),观察者发出执行某命令的指令时,多个绑定的操作则会一起执行。
  • 事件队列模式。命令(即事件)包含了执行对象和操作的信息,当程序要执行某一个命令,则将该命令入队,并等待出队后的执行。

示例

  我们实现一个[音频播放事件队列]作为事件队列模式的示例。下面是一些细节信息:

  • 播放音效需要三个步骤

    • 根据soundId获得资源

    • 判断当前是否可用的音频信道(硬件是否符合播放的条件)

    • 获得可用音频信道后调用播放函数
      以下为相关代码

  • 事件队列的逻辑结构为——环状缓冲区队列,相较于线性表,使用环状缓冲区的好处为出队操作队内元素不用移动。

class AudioPlayCommand
{
    SoundId id;
    float volume;
}

class AudioSystem
{
    public:
        static init()
        {
            head=0;
            tail=0;
        }
        
        //此方法没有立即执行播放,而是使用队列模式——先入队,让事件队列处理
        void PlaySound(SoundId id,float volume)
        {
            //断言事件队列是否已经满了
            assert((tail+1)%MAX_SIZE!=HEAD);
            
            commandQueue[tail].id=id;
            commandQueue[tail].volume=volume;
            tail=(tail+1)%MAX_SIZE;
        }
        
        
        //此函数将在游戏循环中(直接或间接)调用
        void Update()
        {
            //队列中无命令添加
            if(head==tail) return;
            
            Resource music=Sound.FindById(commandQueue[head].id);
            int chanel=AudioSystem.FindOpenChannel();
            
            //播放音频的条件还没有达到
            if(channel==-1)
                return;
                
            //条件达到,播放音乐
            StartSound(music,chanel,commandQueue[head].volume);
            head=(head+1)%MAX_SIZE;
        }
        
        //找打开的信道
        int FindOpenChannel()
        {
            //To Do...
        }
        
    private:
        //环状缓冲区的头索引和尾索引
        static int head;
        static int tail;
        
        const static int MAX_SIZE=20;
        static AudioPlayCommand commandQueue[MAX_SIZE];  //命令队列
}

注意:

事件队列在不同线程中请求入列操作时,要注意同步的问题。

posted @ 2021-10-30 17:04  ZhuSenlin  阅读(98)  评论(0编辑  收藏  举报