《游戏编程模式》(8)

来源:互联网 发布:编程图标 编辑:程序博客网 时间:2024/06/06 00:37

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

 

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   union37   {38     // State when it's in use.39     struct40     {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. 移动对象等于一次删除和一次添加
0 0
原创粉丝点击