设计考虑:
我不建议从图形表示中继承游戏对象。为什么?您可能希望有一个游戏对象的多个图形表示(比如在游戏视图中显示一个,在小地图中显示另一个,或者其他)。关系是“玩家拥有一个图形表示”而不是“玩家是一个图形表示”。更好的解决方案是使用组合而不是继承。其他不错的效果包括可能封装其他碰撞检测(如果您对Qt提供的检测不满意)、解耦等。但对于简单游戏来说,这也可能已经足够了。
对于足够简单的游戏逻辑,可以使用继承,其中其他对象会对主动对象做出反应。但这对于任何更复杂的游戏机制来说可能过于简单了。
class Asteroid {
public:
virtual void CollideWithPlayer(Player&) { p.loseHealth(100); }
};
class ExplodingAsteroid: Asteroid {
public:
virtual void CollideWithPlayer(Player&) { explode(); p.loseHealth(1000); }
};
如果交互变得复杂(许多活动对象表现出自己的行为),您可能需要识别您的对象:
- 有RTTI,但很难推荐,请参见:
RTTI费用高吗?简而言之:昂贵,难以维护。
- 您可以使用双重分派。使用两个虚拟调用来识别对象。
问题:相当多的语法,有时难以维护(特别是当您添加新对象时),所有权问题(请参阅更多)。
维基百科上的游戏示例:
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;
}
};
虚函数标识符
class GameObject() {
virtual getId() { return GAME_OBJECT; }
};
class Asteroid() {
virtual getId() { return ASTEROID; }
};
作为会员或者成员
class GameObject() {
ID getId() { return id; }
protected:
GameObject(ID id):id(id) {}
private:
ID id;
};
或者使用自动初始化id的模板(语法有点令人费解,我们先不考虑它 :o)
现在来看这样的游戏循环:
for each object
update by (fixed) time step
detect collisions and resolve them
您将遇到以下问题:
所有权问题:
当玩家被小行星击中并摧毁时,玩家会失去生命值。
Asteorid::collideWithPlayer(Player& p) { p.loseHealth(100)
现在考虑以下内容。
Player::collideWithAsteroid(Asteroid& a) { this->loseHealth(100)
结果:代码重复或游戏机制不清晰
穷人的解决方案:找别人帮忙 :o)
Asteorid::collideWithPlayer(Player& p) { resolveCollision(p, *this); }
Player::collideWithAsteroid(Asteroid& a) { resolveCollision(*this, a); }
resolveCollision(Player, Asteroid) { p.loseHealth(100); a.explode(); }