zoukankan      html  css  js  c++  java
  • 游戏编程模式之事件队列模式

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

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

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

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

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

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

    示例

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

    • 播放音效需要三个步骤

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

    注意:

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

  • 相关阅读:
    Python学习札记(十五) 高级特性1 切片
    LeetCode Longest Substring Without Repeating Characters
    Python学习札记(十四) Function4 递归函数 & Hanoi Tower
    single number和变体
    tusen 刷题
    实验室网站
    leetcode 76. Minimum Window Substring
    leetcode 4. Median of Two Sorted Arrays
    leetcode 200. Number of Islands 、694 Number of Distinct Islands 、695. Max Area of Island 、130. Surrounded Regions 、434. Number of Islands II(lintcode) 并查集 、178. Graph Valid Tree(lintcode)
    刷题注意事项
  • 原文地址:https://www.cnblogs.com/ZhuSenlin/p/15481521.html
Copyright © 2011-2022 走看看