Design considerations:
I can't recommend inheriting Game objects from their graphic representation. Why? You may want to have multiple graphic representations of one game object (like one in game view or another one in minimap, or whatever). The relation is "Player 'has-a' graphic representation" and not "Player 'is-a' graphic representation". Better solution is to use composition and not inheritance. Other nice effect is possible encapsulation of other collision detection if you are not happy with one provided by Qt, decoupling, ... Truth also is, that for simple game it can be sufficient though.
For simple enough game logic, inheritance where other objects react to active object. Probably too simplistic for any more complex game mechanics.
class Asteroid {
public:
virtual void CollideWithPlayer(Player&) { p.loseHealth(100); }
};
class ExplodingAsteroid: Asteroid {
public:
virtual void CollideWithPlayer(Player&) { explode(); p.loseHealth(1000); }
};
If interaction gets complex(many active objects behaving on their own) you may need to identify your objects:
There's is RTTI, but erm it's hard to recommend see: http://stackoverflow.com/questions/579887/how-expensive-is-rtti
In short: expensive, hard to maintain.
You can use double-dispatch. Identifies objects using two virtual calls.
Problems: Quite a bit of syntax, sometimes difficult to maintain (especially when you add new objects), ownership problems (see more).
Game example from Wikipedia:
class SpaceShip {};
class GiantSpaceShip : public SpaceShip {};
class Asteroid {
public:
virtual void CollideWith(SpaceShip&) {
cout << "Asteroid hit a SpaceShip" << endl;
}
virtual void CollideWith(GiantSpaceShip&) {
cout << "Asteroid hit a GiantSpaceShip" << endl;
}
};
class ExplodingAsteroid : public Asteroid {
public:
virtual void CollideWith(SpaceShip&) {
cout << "ExplodingAsteroid hit a SpaceShip" << endl;
}
virtual void CollideWith(GiantSpaceShip&) {
cout << "ExplodingAsteroid hit a GiantSpaceShip" << endl;
}
};
virtual function id
class GameObject() {
virtual getId() { return GAME_OBJECT; }
};
class Asteroid() {
virtual getId() { return ASTEROID; }
};
or as a member
class GameObject() {
ID getId() { return id; }
protected:
GameObject(ID id):id(id) {}
private:
ID id;
};
or using template with auto initialization of id (a little mind-boggling syntax, let's omit it :o)
Now for game loop like this:
for each object
update by (fixed) time step
detect collisions and resolve them
you will encounter:
Ownership problems:
player loses health when being hit by asteroid and asteroid is destroyed afterwards..
Asteorid::collideWithPlayer(Player& p) { p.loseHealth(100); this->explode(); }
now consider also
Player::collideWithAsteroid(Asteroid& a) { this->loseHealth(100); a.explode(); }
result: code duplicity or unclear game mechanics
poor man's solution: call someone else to help you :o)
Asteorid::collideWithPlayer(Player& p) { resolveCollision(p, *this); }
Player::collideWithAsteroid(Asteroid& a) { resolveCollision(*this, a); }
resolveCollision(Player, Asteroid) { p.loseHealth(100); a.explode(); }