How should game objects be aware of each other?
- by Jefffrey
I find it hard to find a way to organize game objects so that they are polymorphic but at the same time not polymorphic.
Here's an example: assuming that we want all our objects to update() and draw(). In order to do that we need to define a base class GameObject which have those two virtual pure methods and let polymorphism kicks in:
class World {
private:
std::vector<GameObject*> objects;
public:
// ...
update() {
for (auto& o : objects) o->update();
for (auto& o : objects) o->draw(window);
}
};
The update method is supposed to take care of whatever state the specific class object needs to update. The fact is that each objects needs to know about the world around them. For example:
A mine needs to know if someone is colliding with it
A soldier should know if another team's soldier is in proximity
A zombie should know where the closest brain, within a radius, is
For passive interactions (like the first one) I was thinking that the collision detection could delegate what to do in specific cases of collisions to the object itself with a on_collide(GameObject*).
Most of the the other informations (like the other two examples) could just be queried by the game world passed to the update method.
Now the world does not distinguish objects based on their type (it stores all object in a single polymorphic container), so what in fact it will return with an ideal world.entities_in(center, radius) is a container of GameObject*. But of course the soldier does not want to attack other soldiers from his team and a zombie doesn't case about other zombies. So we need to distinguish the behavior. A solution could be the following:
void TeamASoldier::update(const World& world) {
auto list = world.entities_in(position, eye_sight);
for (const auto& e : list)
if (auto enemy = dynamic_cast<TeamBSoldier*>(e))
// shoot towards enemy
}
void Zombie::update(const World& world) {
auto list = world.entities_in(position, eye_sight);
for (const auto& e : list)
if (auto enemy = dynamic_cast<Human*>(e))
// go and eat brain
}
but of course the number of dynamic_cast<> per frame could be horribly high, and we all know how slow dynamic_cast can be. The same problem also applies to the on_collide(GameObject*) delegate that we discussed earlier.
So what it the ideal way to organize the code so that objects can be aware of other objects and be able to ignore them or take actions based on their type?