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 ...
随机推荐
- Python基础之socket编程(Day29)
一.客户端/服务器架构 1.硬件c/s架构(打印机) 2.软件c/s架构 互联网中处处是c/s架构 浏览的网页就是如此 C/S架构与socket的关系 socket就是为了完成c/s架构的开发 二.s ...
- ES集群性能调优链接汇总
1. 集群稳定性的一些问题(一定量数据后集群变得迟钝) https://elasticsearch.cn/question/84 2. ELK 性能(2) — 如何在大业务量下保持 Elasticse ...
- BFC与边距重叠详解
1.什么是BFC? 在解释 BFC 是什么之前,需要先介绍 Box.Formatting Context的概念. Box: CSS布局的基本单位Box 是 CSS 布局的对象和基本单位, 直观点来说, ...
- 快乐学习 Ionic Framework+PhoneGap 手册1-3 {面板切换}
编程的快乐和乐趣,来自于能成功运行程序并运用到项目中,会在后面案例,实际运用到项目当中与数据更新一起说明 从面板切换开始,请看效果图和代码,这只是一个面板切换的效果 Index HTML Code & ...
- Java zip 压缩 文件夹删除,移动,重命名,复制
FileUtil.java import java.io.*; import java.util.List; import java.util.zip.ZipEntry; import java.ut ...
- memcpy与memmove
函数原型: void* memcpy(void *dst,void const *src,size_t count) void* memmove(void *dst,void const *src,s ...
- python的一些内置函数
最近看到一些人写的文章里有提到python的描述符__get__,__set__,__del__. 这里我也小小研究了一下,除了这3个之外还加上过程中学习的几个,比如__call__等. __get_ ...
- 如何修改windows系统远程桌面默认端口
此文档概述如何修改windows系统远程桌面的默认端口,众所周知windows系统默认的远程桌面端口是3389,这样对于开启远程桌面的计算机有一定的安全威胁,修改远程桌面的默认端口可以提高系统的安全性 ...
- sg函数的应用
刚刚接触到sg函数突然感觉到原来可以这么好用,sg函数应该算是博弈论中比较经典的东西了.下面来说说sg函数: 从网上搜集资料终于能看懂了下面解释来自http://www.cnblogs.com/cj6 ...
- 【bzoj2151】种树(堆/优先队列+双向链表)
题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2151 这道题因为优先队列不怎么会用,而且手写堆的代码也不长,也想复习一下手写堆的写法…… ...