15_游戏编程模式EventQueue
#### 两个例子
.GUI event loop ```
while (running)
{
// 从事件队列里获取一个事件
Event event = getNextEvent();
// Handle event...
}
```
.Central event bus
不同系统公用的通信中心 #### 有问题的code ```
class Audio
{
public:
static void playSound(SoundId id, int volume);
}; class Audio
{
public:
static void playSound(SoundId id, int volume);
}; class Menu
{
public:
void onSelect(int index)
{
Audio::playSound(SOUND_BLOOP, VOL_MAX);
// Other stuff...
}
}; ```
问题: . api同步调用, 阻塞到audio处理完请求
. 多个请求不能合并处理
. 处理请求有可能运行在错误的线程上(没有锁) ####模式定义:
```
一系列的通知或请求存储在先进先出的队列里. 发送通知进行入队; 请求处理者从队列里获取请求.
``` ##### 何时使用
```
.如果只是想从sender那里获取消息,使用 observer 或者command将会更简单.
.当你需要push什么到另一个模块,或者pull什么从另一个地方的时候, 你需要一个buffer, 此时就需要一个队列了.
. 队里提供的pull操作, receiver可以延迟处理,合并请求, 或者丢弃. pull请求不开放给sender使用,当sender需要获得响应的时候,队列就有点技穷了.(send then pray)
``` #### 注意事项
```
.中心事件队列是个全局变量
.世界状态会改变,(队列处理不是及时的)
.困在反馈循环里(a ->b -> a ->b ...).如果是同步队列的,你会很快的发现循环bug.
一般原则: 避免在在处理事件的函数里发送事件.
``` #### Sample Code ```
struct PlayMessage
{
SoundId id;
int volume;
}; class Audio
{
public:
static void init()
{
numPending_ = ;
} // Other stuff...
private:
static const int MAX_PENDING = ; static PlayMessage pending_[MAX_PENDING];
static int numPending_;
}; void Audio::playSound(SoundId id, int volume)
{
assert(numPending_ < MAX_PENDING); pending_[numPending_].id = id;
pending_[numPending_].volume = volume;
numPending_++;
} class Audio
{
public:
static void update()
{
for (int i = ; i < numPending_; i++)
{
ResourceId resource = loadSound(pending_[i].id);
int channel = findOpenChannel();
if (channel == -) return;
startSound(resource, channel, pending_[i].volume);
} numPending_ = ;
} // Other stuff...
}; ``` ##### ring buffer 循环buffer ```
class Audio
{
public:
static void init()
{
head_ = ;
tail_ = ;
} // Methods...
private:
static int head_;
static int tail_; // Array...
}; void Audio::playSound(SoundId id, int volume)
{
assert((tail_ + ) % MAX_PENDING != head_); // Add to the end of the list.
pending_[tail_].id = id;
pending_[tail_].volume = volume;
tail_ = (tail_ + ) % MAX_PENDING;
} void Audio::update()
{
// If there are no pending requests, do nothing.
if (head_ == tail_) return; ResourceId resource = loadSound(pending_[head_].id);
int channel = findOpenChannel();
if (channel == -) return;
startSound(resource, channel, pending_[head_].volume); head_ = (head_ + ) % MAX_PENDING;
}
``` ##### 合并请求 ```
void Audio::playSound(SoundId id, int volume)
{
// Walk the pending requests.
for (int i = head_; i != tail_;
i = (i + ) % MAX_PENDING)
{
if (pending_[i].id == id)
{
// Use the larger of the two volumes.
pending_[i].volume = max(volume, pending_[i].volume); // Don't need to enqueue.
return;
}
} // Previous code...
}
``` ##### 多线程 push pull操作需要线程安全 #### 队列里保存的是什么 ```
event or message
event queue(一对多)
描述一些已经发生的事情, 类似异步的observer模式
.允许多个监听者, 队列里保存的都是*已经发生的事件*, 发送者不关心谁去接受它.
.作用域更广.被用于广播一类的事情.趋向于全局可见.
message queue(多对一)
更趋向于只有一个监听者. 多个请求从不同的地方发来,一个处理者进行处理
``` #### 谁可以读队列 ```
单播队列:
.队列实现读取. 发送者只管发送
.队列被封装的更好
.没有读取竞争(决定是广播还是挨个分配)
广播队列:
.如果没有监听者,event被丢弃
.你可能会需要一个事件过滤
工作队列:
类似广播队列,比如worker pool
.需要调度
``` #### 谁可以写队列 ```
一个写者(类似同步observer)
.你明确知道事件是谁发出的
.通常允许多个读者
多个写者
.注意循环
.需要有访问发送者的途径(事件里包含sender的引用)
``` #### 队里里对象的生命周期 ```
. 转移所有权. 有发送者转给队列,队列转给接受者
. 共享所有权.
. 所有权只给队列. (队列申请内存,然后发送者填充数据, 接受者得到引用)
``` ###See also ```
. 队列很像是异步的observer
. message queue, pub sub
. 有限状态机, 状态机需要输入,如果你想异步执行,可以使用队列. 如果你需要多个状态机互相发消息, 需要一个queue接受输入(mail box), 这叫做actor model
. go语言内置的channel本质上就是个 事件或消息 队列.
```
15_游戏编程模式EventQueue的更多相关文章
- 游戏编程模式KeyNote
[游戏编程模式KeyNote] 1.命令模式. 重做在游戏中并不常见,但重放常见.一种简单的重放实现是记录游戏每帧的状态,这样它可以回放,但那会消耗太多的内存.相反,很多游戏记录每个实体每帧运行的命令 ...
- 游戏编程模式 Game Programming Patterns (Robert Nystrom 著)
第1篇 概述 第1章 架构,性能和游戏 (已看) 第2篇 再探设计模式 第2章 命令模式 (已看) 第3章 享元模式 (已看) 第4章 观察者模式 (已看) 第5章 原型模式 (已看) 第6章 单例模 ...
- 16_游戏编程模式ServiceLocator 服务定位
####简单说,就是某个系统作为一个服务,对全局系统可见. Service Locator (服务定位) ``` //简单粗暴的代码, 使用声音系统 // Use a static class? Au ...
- DirectX游戏编程入门
刚开始学习D3D,安装完DirectX9后,在VS2008中新建Win32项目· ----------------------------------------------------------- ...
- DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)
本文由哈利_蜘蛛侠原创,转载请注明出处.有问题欢迎联系2024958085@qq.com 注:我给的电子版是700多页,而实体书是800多页,所以我在提到相关概念的时候 ...
- PC游戏编程(入门篇)(前言写的很不错)
PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...
- 游戏编程算法与技巧 Game Programming Algorithms and Techniques (Sanjay Madhav 著)
http://gamealgorithms.net 第1章 游戏编程概述 (已看) 第2章 2D图形 (已看) 第3章 游戏中的线性代数 (已看) 第4章 3D图形 (已看) 第5章 游戏输入 (已看 ...
- 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记5——Direct3D中的顶点缓存和索引缓存
第12章 Direct3D绘制基础 1. 顶点缓存 计算机所描绘的3D图形是通过多边形网格来构成的,网网格勾勒出轮廓,然后在网格轮廓的表面上贴上相应的图片,这样就构成了一个3D模型.三角形网格是构建物 ...
- 3D游戏编程大师技巧──2D引擎的编译问题
接上一篇文章,这里将介绍2D引擎的编译,从现在开始才真正进入<3D游戏编程大师技巧>的学习.本书的第一.二章只是简介了游戏编程和windows编程,从第三章开始才是介绍<window ...
随机推荐
- 算法寒假实习面试经过之 滴滴(电话一面二面 offer)
一面:1h 介绍比赛项目. lr与xgb的区别? xgb 为什么不用归一化,onehot? xgb 与 gbdt的区别. 做这些比赛你们的优势在哪,既然全是相同的套路. RCNN的原理, CNN的原理 ...
- HackerRank - fibonacci-modified 【大数】
思路 用PYTHON 或 JAVA 干掉 AC代码 a, b, n = map(int, input().split()) for i in range (2, n, 1) : temp = b b ...
- EF删除集中方法对比
// DELETE api/<controller>/5 [HttpGet] public void delete(string id) { #region 官方推荐写法 /* var a ...
- PHP面向对象之对象和引用
在PHP中对象类型和简单变量类型表现可以说是大相径庭,很多数据类型都要可以在写时进行复制,如当写代码$a=$b时,两个变量因为赋予相同的值而告终.所以需要注意的是,这种情况用在对象时就会完全不同了. ...
- 深入理解JVM1
1 Java技术与Java虚拟机 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言.Java类文件格式.Java虚拟机和Java应用程 ...
- Android编译系统简要介绍【转】
本文转载自:http://blog.csdn.net/luoshengyang/article/details/18466779 在Android源码环境中,我们开发好一个模块后,再写一个Androi ...
- 如何修改windows系统远程桌面默认端口
此文档概述如何修改windows系统远程桌面的默认端口,众所周知windows系统默认的远程桌面端口是3389,这样对于开启远程桌面的计算机有一定的安全威胁,修改远程桌面的默认端口可以提高系统的安全性 ...
- Spring插件的安装与卸载---笔记
Spring插件的安装 1.在eclipse中选择工具菜单Help--->Install New Software选项 2.点击Add, 3.选择插件地址或输入网址,点击 OK . http ...
- spark学习5(hbase集群搭建)
第一步:Hbase安装 hadoop,zookeeper前面都安装好了 将hbase-1.1.3-bin.tar.gz上传到/usr/HBase目录下 [root@spark1 HBase]# chm ...
- MapReduce-二进制输入
Hadoop的MapReduce不只是可以处理文本信息,它还可以处理二进制格式的数据1. 关于SequenceFileInputFormat类Hadoop的顺序文件格式存储二进制的键/值对的序列.由于 ...