我有点傻瓜:说我正在做一个简单的,2D,塞尔达式的游戏.当两个对象发生碰撞时,每个对象都应该有一个结果.然而,当主角与某物碰撞时,他的反应完全取决于他碰撞的物体的类型.如果它是一个怪物,他应该反弹,如果它是一堵墙,没有什么应该发生,如果是用丝带一个神奇的蓝盒子,他应该愈合等(这些只是例子).
我还要指出,这两个东西是碰撞的一部分,也就是说,碰撞事件应该发生作无论在角色和怪物,而不仅仅是一方或另一方.
你会怎么写这样的代码?我能想到的一些令人难以置信的不雅的方式,例如,同样载有全球WorldObject类的虚函数,以确定属性 - 例如,一个GetObjectType()函数(返回整型,字符*S,任何标识对象为怪物,Box或Wall),然后在具有更多属性的类中,比如Monster,可能会有更多的虚函数,比如GetSpecies().
但是,这会使维护变得烦人,并导致冲突处理程序中的大型级联切换(或If)语句
MainCharacter::Handler(Object& obj)
{
switch(obj.GetType())
{
case MONSTER:
switch((*(Monster*)&obj)->GetSpecies())
{
case EVILSCARYDOG:
...
...
}
...
}
}
Run Code Online (Sandbox Code Playgroud)
还有使用文件的选项,文件将包含以下内容:
Object=Monster
Species=EvilScaryDog
Subspecies=Boss
Run Code Online (Sandbox Code Playgroud)
然后代码可以检索属性,而不需要虚拟函数混乱一切.但是,这并不能解决级联If问题.
再有就是具有每种情况下的功能的选项,说CollideWall(),CollideMonster(),CollideHealingThingy().这是我个人最不喜欢的(虽然它们都远非可爱),因为它似乎是最难以维护的.
请问有人可以深入了解这个问题的更优雅的解决方案吗?感谢您的帮助!
Kar*_*nek 11
反之亦然 - 因为如果角色与对象发生碰撞,对象也会与角色发生碰撞.因此,您可以拥有一个基类Object,如下所示:
class Object {
virtual void collideWithCharacter(MainCharacter&) = 0;
};
class Monster : public Object {
virtual void collideWithCharacter(MainCharacter&) { /* Monster collision handler */ }
};
// etc. for each object
Run Code Online (Sandbox Code Playgroud)
通常在OOP设计中,虚拟函数是这种情况下唯一的"正确"解决方案:
switch (obj.getType()) {
case A: /* ... */ break;
case B: /* ... */ break;
}
Run Code Online (Sandbox Code Playgroud)
编辑:
澄清后,你需要调整上面的一点.本MainCharacter应具有重载每个它能与碰撞对象的方法:
class MainCharacter {
void collideWith(Monster&) { /* ... */ }
void collideWith(EvilScaryDog&) { /* ... */ }
void collideWith(Boss&) { /* ... */ }
/* etc. for each object */
};
class Object {
virtual void collideWithCharacter(MainCharacter&) = 0;
};
class Monster : public Object {
virtual void collideWithCharacter(MainCharacter& c)
{
c.collideWith(*this); // Tell the main character it collided with us
/* ... */
}
};
/* So on for each object */
Run Code Online (Sandbox Code Playgroud)
这样您就可以通知主角关于碰撞,并且可以采取适当的措施.此外,如果您需要一个不应该通知主角有关冲突的对象,您只需删除该特定类中的通知调用即可.
这种方法称为双重调度.
我还会考虑将MainCharacter本身设为一个Object,将重载移到Object并使用collideWith而不是collideWithCharacter.