Method
Simulate a collection of independent objects by telling each to process one frame of behavior at a time.
Let’s create some skeletons that guard an entrance:
while(true) {
for (double x = 0; x < 100; x++) skeleton.setX(x);
for (double x = 100; x > 0; x--) skeleton.setX(x);
}
Of course, the player doesn’t see this. It’s locked in an infinite loop.
Instead, we’ll rely on the outer loop for iteration.
;
Entity skeletonbool patrollingLeft = false;
double x = 0;
while (true) {
if (patrollingLeft) {
--;
xif (x == 0) patrollingLeft = false;
} else {
++;
xif (x == 100) patrollingLeft = true;
}
.setX(x);
skeleton}
Then we’ll have to add some more enchanted statues…
;
Entity leftStatue;
Entity rightStatueint leftStatueFrames = 0;
int rightStatueFrames = 0;
// Main game loop:
while (true) {
if (++leftStatueFrames == 90) {
= 0;
leftStatueFrames .shootLightning();
leftStatue}
if (++rightStatueFrames == 80) {
= 0;
rightStatueFrames .shootLightning();
rightStatue}
// Handle user input and render game...
}
This is maintainable. We have lots of state stuffed in the game loop.
Instead, Each entity in the game should encapsulate its own behavior.
We need an abstraction layer: we create that by defining an abstract
update()
method. The game loop maintains a collection of
objects, and all it does is call update()
on them once,
every frame.
The game world maintains a collection of objects. Each object implements an update method that simulates one frame of the object’s behavior. Each frame, the game updates every object in the collection.
This pattern works well when:
dead
. The update loop
can skip over dead objects, and at the end of the loop, remove
them.class Entity {
public:
() : x_(0), y_(0) {}
Entityvirtual ~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_;
double y_;
};
Then we need a world to hold all of these objects.
class World {
public:
() : numEntities_(0) {}
Worldvoid gameLoop();
private:
* entities_[MAX_ENTITIES];
Entityint numEntities_;
};
And of course, implementing the gameLoop
method:
void World::gameLoop() {
while (true) {
for (const auto entity : entities_) {
entities_.update();
// code to handle if entities are dead
}
}
}
class Skeleton : public Entity {
public:
() : patrollingLeft_(false) {}
Skeleton
virtual void update() {
if (patrollingLeft_) {
(x() - 1);
setXif (x() == 0) patrollingLeft_ = false;
}
else {
(x() + 1);
setXif (x() == 100) patrollingLeft_ = true;
}
}
private:
bool patrollingLeft_;
};
And the statue:
class Statue : public Entity {
public:
(int delay) : frames_(0), delay_(delay) {}
Statue
virtual void update() {
if (++frames_ == delay_) {
();
shootLightning
// Reset the timer.
frames_ = 0;
}
}
private:
int frames_;
int delay_;
void shootLightning() { /* Shoot the lightning... */ }
};
If we allow our game to have variable time pass in the game loop, we can pass that in:
void Skeleton::update(double elapsed) {
if (patrollingLeft_) {
-= elapsed;
x if (x <= 0) {
patrollingLeft_ = false;
= -x;
x }
}
else {
+= elapsed;
x if (x >= 100) {
patrollingLeft_ = true;
= 100 - (x - 100);
x }
}
}
Dormant objects can be handled by setting a flag that says whether or not they’re alive (simpler) or you can separate entities into a collection of alive or not.