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


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

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

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

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

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

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

    示例

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

    • 播放音效需要三个步骤

      • 根据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];  //命令队列
    }
    

    注意:

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

  • 相关阅读:
    获取定位
    关于meta 总结
    关于微信 ios的部分兼容(摇动播放)
    mysql
    js_DOM的导航属性--Dom_event事件
    IO阻塞与IO非阻塞2
    进程池
    生产消费者模型
    队列----------------多线程利器
    信号量
  • 原文地址:https://www.cnblogs.com/ZhuSenlin/p/15481521.html
Copyright © 2020-2023  润新知