如何写出优雅的碰撞处理机制?

bfo*_*ops 11 c++ collision

我有点傻瓜:说我正在做一个简单的,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.