《游戏编程模式》
Posted pandawuwyj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《游戏编程模式》相关的知识,希望对你有一定的参考价值。
《游戏编程模式》最后一篇,刚从英国玩了一圈,春节又要到啦
Chapter 19 对象池
使用固定的对象池重用对象,取代单独地分配和释放对象,达到提升性能和优化内存使用的目的。
使用情境:
- 频繁创建销毁对象;
- 对象大小基本一致;
- 堆上分配内存较慢或可能产生内存碎片;
粒子类:
用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). 不创建了(特效叠加时…);
(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 }
四叉树:
如果空间中的对象超过阈值,空间就一切四
- 添加单个对象不会产生一次以上的拆分动作
- 删除对象需要判断父区域对象总数,如果低于阈值则合并区域
- 移动对象等于一次删除和一次添加
以上是关于《游戏编程模式》的主要内容,如果未能解决你的问题,请参考以下文章