# 更新方法

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

比如有一个NPC，我们的代码肯定不能这样写

```cpp
while(true)
{
    // Patrol right
    
    for (double x = 0; x < 100; x++) skeleton.setX(x);
    
    // Patrol left

    for (double x = 100; x > 0; x--) skeleton.setX(x);
}
```

程序被一个死循环锁住，这显然是很差劲的游戏体验，我们所希望的是骷髅兵每一帧走一步。

```cpp
Entity skeleton; 
bool patrollingLeft = false; 
double x = 0;　

// Main game loop:

while (true) 
{　 
　if (patrollingLeft)　// 向左巡逻 
　{　　
　　 x--;　　
　　 if (x == 0) patrollingLeft = false;　
　 }
　 else // 向右巡逻
　{　　 
　　x++;　　
　　if (x == 100) patrollingLeft = true;　 
　}　
　skeleton.setX(x);　　

　// Handle user input and render game...
 } 
```

将游戏逻辑直接塞到一个循环中，很明显没法维护。

更新方法模式在如下情景最为适用：

* 你的游戏中含有一系列对象或系统需要同步地运转
* 各个对象之间地行为几乎是相互独立的
* 对象的行为与时间相关

```cpp
int numObjectsThisTurn = numObjects_;
for (int i = 0; i < numObjectsThisTurn; i++) 
{　 
　objects_[i]−>update(); 
}
```

一个令人担忧的问题是在迭代时移除对象，比如进行下标i的update，然后删除下标i,然后我们i++了，就会造成漏处理一个对象，它被跳过了。

一个简便的解决方法是当你更新时从表的末尾开始遍历，此方法下移除对象，只会让已经更新的物品发生移动，永远向下标小的对象遍历，
我们永远不会提前删除未遍历到的下标小的。

一个Entity类

```cpp
class Entity
{ 
public:
　Entity()　 
　: x_(0), y_(0)　 {}　　

　virtual ~Entity() {}　 
　virtual void update() = 0;　　

　double x() const { return x_; }　 
　double y() const { return y_; }　　

　void setX(double x) { x_ = x; }　 
　void setY(double y) { y_ = y; }　

private:
　double x_,y_; 
};
```

World类

```cpp
class World
{ 
public:
　World()　 
　: numEntities_(0)　 {}　　

　void gameLoop();　

private:
　Entity* entities_[MAX_ENTITIES];　 
　int numEntities_; 
};
```

World的gameLoop就是游戏逻辑主循环

```cpp
void World::gameLoop() 
{　 
　while (true)　 
　{　　 
　　// Handle user input...



　　// Update each entity.


　　for (int i = 0; i < numEntities_; i++)　　
　　{　　　 
　　　entities_[i]->update();
　　}　　　

　　// Physics and rendering...

 
　} 
} 
```

## 定义实体继承Entity

定义一个骷髅守卫

```cpp
class Skeleton : public Entity 
{
public:
　Skeleton()　 
　: patrollingLeft_(false)　{}　　

　virtual void update()　
　{　　 
　　if (patrollingLeft_)　　 
　　{　　　 
　　　setX(x() − 1);　　　 
　　　if (x() == 0) patrollingLeft_ = false;　　 
　　}　　 
　　else
　　{　　　 
　　　setX(x() + 1);　　　 
　　　if (x() == 100) patrollingLeft_ = true;　　 
　　}
　}　
private:
　bool patrollingLeft_; 
};
```

定义防守雕像

```cpp
class Statue : public Entity 
{
public:
　Statue(int delay)　 
　: frames_(0),　　 
　　delay_(delay)　 
　{}　　

　virtual void update()　 
　{
    // 跌倒多少逻辑帧然后就调用一下shootLightning
　　if (++frames_ == delay_)　　 
　　{　　　 
　　　shootLightning();　　　　

　　　// Reset the timer.


　　　frames_ = 0;　　 
　　}　 
　}　

private:
　int frames_;　
　int delay_;
　void shootLightning()　 
　{　　 
　　// Shoot the lightning...

　} 
}; 
```

## 逝去的时间

这就又扯到前面的游戏循环了，至此我们假设每次对 update 的调用会让整个游戏世界向前推进相同固定的时间长度。

```cpp
void Skeleton::update(double elapsed) 
{　 
　if (patrollingLeft_)　 
　{　　 
　　x - = elapsed;　　 
　　if (x <= 0)　　 
　　{　　　 
　　　patrollingLeft_ = false;　　　 
　　　x = - x;　　 
　　}　 
}　 
else
{　　
　　 x += elapsed;　　 
　　if (x >= 100)　　 
　　{　　　 
　　　patrollingLeft_ = true;　　　
　　　x = 100 - (x - 100);　　 
　　}　 
　} 
}
```

现在，骷髅移动的距离随着时间间隔而增长，同样能看到处理变时步长时额外增加的复杂度。骷髅可能在很长的时间差下超出其巡逻范围，我们需要小心地对这一情况进行处理。

## update方法依存于何类中

* 实体类中，上面的逻辑直接跑在 Entity 的update中。
* 组件类中

在entity下挂载很多组件，然后Entity的update调用所有组件的update，向下面这样

```cpp
class Entity{
    int cmptNum;
    Cmpt* cmptArr[MAX_LEN];
};
void Entity::update()
{
    for(int i = 0; i < cmptNum; i++)
    {
        cmptArr[i]->update();
    }
}
```

* 代理类中

```cpp
void Entity::update()
{
    // Forward to state object
    state_->update();
}
```

## 那些未被利用的对象该如何处理

常需要在游戏中维护这样一些对象：不论出于何种原因，它们暂时无需被更新。它们可能被禁用，被移除出屏幕，或者至今尚未解锁。假如大量的对象处于这种状态，则可能会导致CPU每一帧都浪费许多时间来遍历这些对象却毫无作为。

一种方法是单独维护一个需要被更新的“存活”对象表。当一个对象被禁用时，将它从其中移除。当它重新被启用时，把它添加回表中。这样做，你只需遍历那些实际上有作为的对象即可。

* 假设你使用单个集合来存储所有游戏对象，这比较浪费时间，需要检查“是否死亡”的标志。
* 假设你使用一个单独的集合来维护活跃的对象，这比较浪费空间，需要维护两个集合。
