首页 > 代码库 > 《游戏编程模式》(4)

《游戏编程模式》(4)

Chatper 8 双缓冲

核心问题:对状态同时进行修改与访问的冲突(读写)

 

缓冲区:

 1 class Framebuffer
 2 {
 3 
 4 public:
 5   Framebuffer() { clear(); } 
 6 
 7   void clear()
 8   {
 9     for (int i = 0; i < WIDTH * HEIGHT; i++)
10     {
11       pixels_[i] = WHITE;
12     }
13   } 
14 
15   void draw(int x, int y)
16   {
17     pixels_[(WIDTH * y) + x] = BLACK;
18   }
19 
20   const char* getPixels()
21   {
22     return pixels_;
23   }
24 
25 private:
26   static const int WIDTH = 160;
27   static const int HEIGHT = 120;
28  
29   char pixels_[WIDTH * HEIGHT];
30 };

 

Clear、draw、swap:

 1 class Scene
 2 {
 3 
 4 public:
 5   Scene()
 6   : current_(&buffers_[0]),
 7     next_(&buffers_[1])
 8   {} 
 9 
10   void draw()
11   {
12     next_->clear(); 
13 
14     next_->draw(1, 1);
15     // ...
16     next_->draw(4, 3); 
17 
18     swap();
19   }
20 
21   Framebuffer& getBuffer() { return *current_; }
22 
23 
24 private:
25   void swap()
26   {
27     // Just switch the pointers.
28     Framebuffer* temp = current_;
29     current_ = next_;
30     next_ = temp;
31   }
32 
33   Framebuffer  buffers_[2];
34   Framebuffer* current_;
35   Framebuffer* next_;
36 
37 };

交换缓冲区指针或者引用:

  1. 快;
  2. 没有真正地移动数据;
  3. 缓冲区现存数据来自两帧之前(绘制第三帧前,当前缓冲区数据存储的是第一帧数据,第二帧数据在另一缓冲区里);
  4. 缓冲区是一个整体

 

另一个例子(缓存巴掌不受他们在舞台上顺序的影响):

 1 class Actor
 2 {
 3 
 4 public:
 5   Actor() : currentSlapped_(false) {}
 6 
 7   virtual ~Actor() {}
 8   virtual void update() = 0;
 9  
10   void swap()
11   {
12     // Swap the buffer.
13     currentSlapped_ = nextSlapped_; 
14 
15     // Clear the new "next" buffer.
16     nextSlapped_ = false;
17   } 
18 
19   void slap()       { nextSlapped_ = true; }
20   bool wasSlapped() { return currentSlapped_; }
21 
22 private:
23   bool currentSlapped_;
24   bool nextSlapped_;
25 
26 };
27 
28 class Stage
29 {
30 
31 public:
32   void add(Actor* actor, int index)
33   {
34     actors_[index] = actor;
35   }
36 
37   void update()
38   {
39     for (int i = 0; i < NUM_ACTORS; i++)
40     {
41       actors_[i]->update();
42     }
43 
44     for (int i = 0; i < NUM_ACTORS; i++)
45     {
46       actors_[i]->swap();
47     }
48   }
49 
50 private:
51   static const int NUM_ACTORS = 3;
52 
53   Actor* actors_[NUM_ACTORS];
54 
55 };

缓冲区之间进行数据拷贝:

  1. 慢(数据量大的话);
  2. 缓冲区内的数据和当前数据只差一帧;
  3. 许多对象各持有一块缓冲区数据

 

Chatper 9 游戏循环

实现用户输入和处理器速度在游戏行进时间上的解耦。

一个游戏循环在游戏过程中持续地运转。每循环一次,它非阻塞地处理用户的输入,更新游戏状态,并渲染游戏。它跟踪流逝的时间并控制游戏的速率。

 

定时更新迭代,变时渲染:

 1 double previous = getCurrentTime();
 2 double lag = 0.0;
 3 
 4 while (true)
 5 {
 6   double current = getCurrentTime();
 7   double elapsed = current - previous;
 8   previous = current;
 9   lag += elapsed; 
10 
11   processInput();
12 
13   while (lag >= MS_PER_UPDATE)
14   {
15     update();
16     lag -= MS_PER_UPDATE;
17   }
18 
19   render(lag / MS_PER_UPDATE);
20 }
  1. 在每帧的开始,基于实际流逝的时间更新lag,lag表示游戏时间相对现实时间落后的差量;
  2. 使用一个内部循环来更新游戏,每次以固定时长进行,直到它追上现实时间;
  3. 一旦追上现实时间,开始渲染并进行下一次游戏循环 

总结:

以固定时间步长进行更新,将渲染和更新分离,并让渲染来跟进玩家的游戏时钟;在快硬件平台上十分顺畅,在慢硬件平台上有些许卡顿来保证游戏状态实时更新。

 

Chatper 10 更新方法

通过对所有对象实例同时进行帧更新来模拟一系列相互独立的游戏对象。

使用情境:

  1. 对象的行为与时间相关;
  2. 各个对象之间的行为几乎相对独立;
  3. 一系列对象或系统需要同步地运转。

为游戏中的每个实体封装其自身的行为,使游戏循环保持简洁且便于往循环中增加或删除实体。

 

实体类:

 1 class Entity
 2 {
 3 
 4 public:
 5   Entity()
 6   : x_(0), y_(0)
 7   {} 
 8 
 9   virtual ~Entity() {}
10   virtual void update() = 0; 
11 
12   double x() const { return x_; }
13   double y() const { return y_; }
14  
15   void setX(double x) { x_ = x; }
16   void setY(double y) { y_ = y; }
17 
18 private:
19   double x_;
20   double y_;
21 
22 };

定义子类实体:

 1 class Skeleton : public Entity
 2 {
 3 
 4 public:
 5   Skeleton()
 6   : patrollingLeft_(false)
 7   {} 
 8 
 9   virtual void update()
10   {
11     if (patrollingLeft_)
12     {
13       setX(x() - 1);
14       if (x() == 0) patrollingLeft_ = false;
15     }
16     else
17     {
18       setX(x() + 1);
19       if (x() == 100) patrollingLeft_ = true;
20     }
21   }
22 
23 private:
24   bool patrollingLeft_;
25 
26 };
27 
28 class Statue : public Entity
29 {
30 
31 public:
32   Statue(int delay)
33   : frames_(0),
34     delay_(delay)
35   {}
36 
37   virtual void update()
38   {
39     if (++frames_ == delay_)
40     {
41       shootLightning();
42  
43       // Reset the timer.
44       frames_ = 0;
45     }
46   }
47 
48 private:
49   int frames_;
50   int delay_; 
51 
52   void shootLightning()
53   {
54     // Shoot the lightning...
55   }
56 
57 };

遍历实体逐帧更新:

 1 class World
 2 {
 3 
 4 public:
 5   World()
 6   : numEntities_(0)
 7   {} 
 8 
 9   void gameLoop(); 
10 
11 private:
12   Entity* entities_[MAX_ENTITIES];
13   int numEntities_;
14 
15 };
16 
17 void World::gameLoop()
18 {
19   while (true)
20   {
21     // Handle user input...
22  
23     // Update each entity.
24     for (int i = 0; i < numEntities_; i++)
25     {
26       entities_[i]->update();
27     } 
28 
29     // Physics and rendering...
30   }
31 }

 

更新期间修改对象:

  1. 增加对象

          遍历前存储当前对象列表的长度;

     2.  删除对象

    (1). 从后往前遍历

    (2). 标记删除然后再遍历删除

《游戏编程模式》(4)