《游戏编程模式》(8)

《游戏编程模式》最后一篇,刚从英国玩了一圈,春节又要到啦

Chapter 19 对象池

使用固定的对象池重用对象,取代单独地分配和释放对象,达到提升性能和优化内存使用的目的。

使用情境:

  1. 频繁创建销毁对象;
  2. 对象大小基本一致;
  3. 堆上分配内存较慢或可能产生内存碎片;

粒子类:

用union节省内存:粒子使用时用live结构体,不使用时用next指针

 1 class Particle
 2 {
 3
 4 public:
 5   Particle()
 6   : framesLeft_(0)
 7   {}
 8
 9   void init(double x, double y,
10             double xVel, double yVel, int lifetime)
11   {
12     x_ = x; y_ = y;
13     xVel_ = xVel; yVel_ = yVel;
14     framesLeft_ = lifetime;
15   }
16
17   bool animate()
18   {
19     if (!inUse()) return false;
20
21     framesLeft_--;
22     x_ += xVel_;
23     y_ += yVel_;
24
25     return framesLeft_ == 0;
26 }
27
28   bool inUse() const { return framesLeft_ > 0; }
29
30   Particle* getNext() const { return state_.next; }
31   void setNext(Particle* next) { state_.next = next; }
32
33 private:
34   int framesLeft_;
35
36   union
37   {
38     // State when it‘s in use.
39     struct
40     {
41       double x, y;
42       double xVel, yVel;
43     } live;
44
45     // State when it‘s available.
46     Particle* next;
47
48   } state_;
49
50 };

粒子对象池:

 1 class ParticlePool
 2 {
 3
 4 public:
 5   void create(double x, double y,
 6               double xVel, double yVel, int lifetime);
 7
 8   void animate()
 9   {
10     for (int i = 0; i < POOL_SIZE; i++)
11     {
12       particles_[i].animate();
13     }
14   }
15
16 private:
17   static const int POOL_SIZE = 100;
18   Particle particles_[POOL_SIZE];
19
20   Particle* firstAvailable_;
21 };
22
23 ParticlePool::ParticlePool()
24 {
25   // The first one is available.
26   firstAvailable_ = &particles_[0];
27
28   // Each particle points to the next.
29   for (int i = 0; i < POOL_SIZE - 1; i++)
30   {
31     particles_[i].setNext(&particles_[i + 1]);
32   }
33
34   // The last one terminates the list.
35   particles_[POOL_SIZE - 1].setNext(NULL);
36
37 }

构造函数中串起一个链表。

创建一个新粒子:

 1 void ParticlePool::create(double x, double y,
 2                           double xVel, double yVel,
 3                           int lifetime)
 4 {
 5   // Make sure the pool isn‘t full.
 6   assert(firstAvailable_ != NULL);
 7
 8   // Remove it from the available list.
 9   Particle* newParticle = firstAvailable_;
10   firstAvailable_ = newParticle->getNext();
11
12   newParticle->init(x, y, xVel, yVel, lifetime);
13 }

动画放完放回粒子池:

 1 void ParticlePool::animate()
 2 {
 3   for (int i = 0; i < POOL_SIZE; i++)
 4   {
 5     if (particles_[i].animate())
 6     {
 7       // Add this particle to the front of the list.
 8       particles_[i].setNext(firstAvailable_);
 9       firstAvailable_ = &particles_[i];
10     }
11   }
12 }
  1. 如果对象尺寸大小不一,可以考虑根据对象的尺寸划分大小不同的池,避免过多的内存浪费;
  2. 向对象池请求失败:

(1). 避免;

(2). 不创建了(特效叠加时…);

(3). 清理现存对象(是否会卡);

(4). 增加对象池大小(适当时机再恢复原大小)

Chapter 20 空间分区

将对象存储在根据位置组织的数据结构中来高效地定位它们。

你有一组对象(可能还挺多),将对象存储在一个根据对象位置来组织的数据结构中,该数据结构可以让你高效查询位于或靠近某处的对象。当对象位置改变时,更新并继续维护该空间数据对象。

用更复杂的数据结构(空间)来换取大量查询时的性能优化(时间)。

Unit类:

 1 class Unit
 2 {
 3   friend class Grid;
 4
 5 public:
 6   Unit(Grid* grid, double x, double y)
 7   : grid_(grid),
 8     x_(x),
 9     y_(y)
10   {}
11
12   void move(double x, double y);
13
14 private:
15   double x_, y_;
16   Grid* grid_;
17
18   Unit* prev_;
19   Unit* next_;
20 };

Grid类:

 1 class Grid
 2 {
 3
 4 public:
 5   Grid()
 6   {
 7     // Clear the grid.
 8     for (int x = 0; x < NUM_CELLS; x++)
 9     {
10       for (int y = 0; y < NUM_CELLS; y++)
11       {
12         cells_[x][y] = NULL;
13       }
14     }
15   }
16
17   static const int NUM_CELLS = 10;
18   static const int CELL_SIZE = 20;
19
20 private:
21   Unit* cells_[NUM_CELLS][NUM_CELLS];
22
23 }; 

cells_是一个双重链表,记录了单元格内的unit链。

初始化:找到单位所在的单元格加到链表前面

 1 Unit::Unit(Grid* grid, double x, double y)
 2   : grid_(grid),
 3     x_(x),
 4     y_(y),
 5     prev_(NULL),
 6     next_(NULL)
 7 {
 8   grid_->add(this);
 9 }
10
11 void Grid::add(Unit* unit)
12 {
13   // Determine which grid cell it‘s in.
14   int cellX = (int)(unit->x_ / Grid::CELL_SIZE);
15   int cellY = (int)(unit->y_ / Grid::CELL_SIZE);
16
17   // Add to the front of list for the cell it‘s in.
18   unit->prev_ = NULL;
19   unit->next_ = cells_[cellX][cellY];
20   cells_[cellX][cellY] = unit;
21
22   if (unit->next_ != NULL)
23   {
24     unit->next_->prev_ = unit;
25   }
26 }

Unit移动:查看是否还在原先的单元格,如果离开了原先的单元格,从链表中移除再add

 1 void Unit::move(double x, double y)
 2 {
 3   grid_->move(this, x, y);
 4 }
 5
 6 void Grid::move(Unit* unit, double x, double y)
 7 {
 8   // See which cell it was in.
 9   int oldCellX = (int)(unit->x_ / Grid::CELL_SIZE);
10   int oldCellY = (int)(unit->y_ / Grid::CELL_SIZE);
11
12   // See which cell it‘s moving to.
13   int cellX = (int)(x / Grid::CELL_SIZE);
14   int cellY = (int)(y / Grid::CELL_SIZE);
15
16   unit->x_ = x;
17   unit->y_ = y;
18
19   // If it didn‘t change cells, we‘re done.
20   if (oldCellX == cellX && oldCellY == cellY) return;
21
22   // Unlink it from the list of its old cell.
23   if (unit->prev_ != NULL)
24   {
25     unit->prev_->next_ = unit->next_;
26   }
27
28   if (unit->next_ != NULL)
29   {
30     unit->next_->prev_ = unit->prev_;
31   }
32
33   // If it‘s the head of a list, remove it.
34   if (cells_[oldCellX][oldCellY] == unit)
35   {
36     cells_[oldCellX][oldCellY] = unit->next_;
37   }
38
39   // Add it back to the grid at its new cell.
40   add(unit);
41
42 }

战斗:查找单元格相邻一半的单元格(4个)

 1 void Grid::handleMelee()
 2 {
 3   for (int x = 0; x < NUM_CELLS; x++)
 4   {
 5     for (int y = 0; y < NUM_CELLS; y++)
 6     {
 7       handleCell(x, y);
 8     }
 9   }
10 }
11
12 void Grid::handleCell(int x, int y)
13 {
14   Unit* unit = cells_[x][y];
15   while (unit != NULL)
16   {
17     // Handle other units in this cell.
18     handleUnit(unit, unit->next_);
19
20     // Also try the neighboring cells.
21     if (x > 0 && y > 0) handleUnit(unit, cells_[x - 1][y - 1]);
22     if (x > 0) handleUnit(unit, cells_[x - 1][y]);
23     if (y > 0) handleUnit(unit, cells_[x][y - 1]);
24     if (x > 0 && y < NUM_CELLS - 1)
25     {
26       handleUnit(unit, cells_[x - 1][y + 1]);
27     }
28
29     unit = unit->next_;
30   }
31 }
32
33 void Grid::handleUnit(Unit* unit, Unit* other)
34 {
35   while (other != NULL)
36   {
37     if (distance(unit, other) < ATTACK_DISTANCE)
38     {
39       handleAttack(unit, other);
40     }
41
42     other = other->next_;
43   }
44 }

四叉树:

如果空间中的对象超过阈值,空间就一切四

  1. 添加单个对象不会产生一次以上的拆分动作
  2. 删除对象需要判断父区域对象总数,如果低于阈值则合并区域
  3. 移动对象等于一次删除和一次添加
时间: 2024-10-12 13:07:39

《游戏编程模式》(8)的相关文章

Game Programming Patterns(游戏编程模式)

Game Programming Patterns(游戏编程模式) 大部分游戏开发者在他们游戏项目上总是一个巨大的挑战,总是东拼西凑,修修补补.很多游戏项目常常以失败告终,或者是被淹没在复杂而繁琐的代码中.如何解决这些问题? 各位看官,不管你是对游戏开发感兴趣,或者正在饱受代码不断增长带来的灾难,这本书将是你们的福音! 这本Game Programming Patterns 是由Bob Nystrom(一位在EA待过7年,有着20年游戏开发经历的工程师编写).本书将告诉你,什么模式能够帮你理清和

Game Programming Patterns(游戏编程模式)-简介

游戏编程模式-简介 本系列博客是:Game Programming Patterns 的中文翻译版本. 翻译的github地址: cyh24. 如有兴趣,可联系博主共同翻译,一起造(wu)福(dao)他人. 博客虽然水分很足,但是也算是博主的苦劳了, 如需转载,请附上本文链接http://blog.csdn.net/cyh_24/article/details/46868419,不甚感激! 本系列博客 目录,可点击进入. 简介 ============================ 在我五年级

【游戏设计模式】之四 《游戏编程模式》读书笔记:全书内容梗概总结

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/53240330 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文的Github版本:QianMo/Reading-Notes/<游戏编程模式>读书笔记 这是一篇超过万字读书笔记,总结了<游戏编程模式>一书中所有章节与内容的知识梗概. 我们知道,游戏行业其实一直很缺一本系

Game Programming Patterns(游戏编程模式)-架构,性能与游戏

游戏编程模式- 架构,性能与游戏 本系列博客是:Game Programming Patterns 的中文翻译版本. 翻译的github地址: cyh24. 如有兴趣,可联系博主共同翻译,一起造(wu)福(dao)他人. 博客虽然水分很足,但是也算是博主的苦劳了, 如需转载,请附上本文链接,不甚感激! 本系列博客 <游戏编程模式>– 目录,可点击进入. 架构,性能与游戏 ============================ 在我们埋头研究一堆的设计模式之前,我想先告诉你,对于软件架构,我个

《游戏编程模式》记录

写在前面 这本书长这样 我还没有看过“GOF”,我所读到的设计模式都是这本书(游戏角度)给出的定义,害怕GOF中的定义过于抽象. 没有在项目代码晃来晃去经历的,或者没有工作至少半年的,不用着急买这本书,因为估计看不懂. 本文用来重点记录“我觉得XX设计模式是什么”,以及“当我在看XX设计模式时,我在想什么” 这本书的写作方式,属于我喜欢的“谈话口吻” 命令模式 书面定义:“将一个请求封装成一个对象,从而允许你使用不同的请求.队列或日志将客户端参数化,同时支持请求操作的撤销和恢复” 他的举例:按键

游戏编程模式--单例模式

单例模式 定义:确保一个类只有一个实例,并为其提供一个全局的访问入口. 那么什么情况下使用单例?最常见的情况就是一个类需要与一个维持自身状态的外部系统进行交互,比如说打印机.大多数情况下都是多人共用一个打印机,这意味着可能由多个人同时向这个打印机发送打印任务,这个时候管理打印机的类就必须熟悉打印机的当前状态并协调这些任务的执行.这个时候就不允许存在多个打印机的实例,因为实例无法知道其他的实例所做的操作,也就无法进行整体的管理. 我们先看看最常见的单例的实现方式: class FileSystem

游戏编程模式-事件队列

“对消息或事件的发送与受理进行事件上的解耦.” 动机 如果你曾从事过用户界面编程,那肯定对“事件”不陌生了.每当你在界面中点击一个按钮或下拉菜单,系统都会生成一个事件,系统会把这个事件抛给你的应用程序,你的任务就是获取到这些事件并将其与你自定义的行为关联起来.那么为了获取到这些事件,你的代码通常都会由个事件循环.就像这样: while(running) { Event e = pollEvent(); //handle event } 可以看到,这段代码会不断的获取事件,然后处理.但如果期间再事

游戏编程模式-对象池

“使用固定的对象池重用对象,取代单独的分配和释放对象,以此来达到提升性能和优化内存使用的目的.” 动机 假设我们正在致力于游戏的视觉效果优化.当英雄释放魔法时,我们想让一个火花在屏幕上炸裂.这通常需要一个粒子系统(一个用来生成大量小的图形并在它们生存周期产生动画的引擎)来实现.而这个粒子系统实现这个火花的时候会产生大量的粒子,我们需要非常快速的创建这些粒子同时在这些粒子“死亡”的时候释放这些粒子对象.在这里,我们会碰到一个严重的问题——内存碎片化. 碎片化地害处 为游戏和移动设备编程在很多方面都

《游戏编程模式》(6)

Chapter 14 组件模式 允许一个单一的实体跨越多个不同域而不会导致耦合. 为实现两个类之间的代码共享,应该让他们拥有同一个类的实例,而不是继承同一个类. 使用情境: 有一个涉及多个域的类.但希望这些域保持解耦: 这个类很庞大: 希望定义许多共享不同能力的对象. 分割不同的域: 1 class InputComponent 2 { 3 4 public: 5 void update(Bjorn& bjorn) 6 { 7 switch (Controller::getJoystickDir