【问题标题】:Game Collision-Handling游戏碰撞处理
【发布时间】:2010-12-05 17:10:38
【问题描述】:

The code given is for example purposes, not copypasta from my codebase

我正在使用 Boost 和 SDL 用 C++ 编写一个原始的跨平台 Snake 游戏,我想知道实现碰撞处理的最佳方法是什么(不是碰撞检测)。到目前为止,我一直在使用单一调度的想法,带有丑陋的意大利面条代码,如下所示:

void Snake::CollisionHandler(const WorldObject& obj)
{
    // collided with self
    if(obj.GetObjectType() == snake)
        Die();
    ...
    ...
}

另外,我有一个“全局”碰撞处理程序,它处理与碰撞有关的事情,这些事情独立于每个对象,例如:


void GameWorld::CollisionHandler(WorldObject& obj1, WorldObject& obj2)
{
    if(obj1.GetObjectType() == snake && obj2.GetObjectType() == snake)
        PlayDeathSound();
    ...
    ...
}

为了避免声音因碰撞而播放两次。

我也考虑过双重调度,如下所示:


void Snake::CollisionHandler(WorldObject& obj) const
{
    // invoke obj's collision handler with a const Snake& parameter
    obj.CollisionHandler(*this);
}
// collided with self
void Snake::CollisionHandler(const Snake& obj)
{
    Die();
}

这也包括一个与上面类似的全局碰撞处理程序。

然后是具有全局碰撞处理程序(它是所有游戏对象的友元函数)的方法,如下所示:


void GameWorld::CollisionHandler(WorldObject& obj1, WorldObject& obj2)
{
    // snake collided with self
    if(obj1.GetObjectType() == snake && obj2.GetObjectType() == snake)
    {
        obj1.Die();
        obj2.Die();
        PlayDeathSound();
    }
    ...
    ...
}

我缺少什么策略吗?其中哪一个最终是最好的?它们似乎都涉及一些丑陋的代码,而单调度和双调度涉及多态性,我个人试图回避这一点。

【问题讨论】:

  • 在您的上一个示例中,注释显示为“snake collides with self”,然后您调用了两次Die()。我怀疑那将是一件坏事。 PS:不需要“加好友”任何东西,只要把它需要的功能公开即可。
  • PPS:为了避免重复代码,我倾向于只将对该对象的效果放在它的“我与 Foo 碰撞”函数中。 EX:当一艘船与子弹相撞时,它会受到一些伤害。当一颗子弹与一艘船相撞时,它就会消失。

标签: c++ collision-detection


【解决方案1】:

冲突处理是 C++/Java 风格的面向对象(使用单一调度)不够灵活的领域。碰撞的结果取决于 both 碰撞对象的类型,因此您需要多次调度来处理它。 (维基百科multiple dispatch 文章中的激励示例是碰撞处理,这绝非巧合!)

我认为,一个全局碰撞处理程序,然后分派给单个对象方法是 C++ 中这种不足的最佳解决方法。

【讨论】:

  • 这就是我现在所拥有的。实际上,在有效的 C++ 中,有一些在 C++ 中获得双重调度的 hacky 方法。其中一个显示在我的原始帖子中(第三块代码)
  • 是的,但我认为在这种情况下这是一个较弱的解决方法。如果您在 A 类中有代码用于与 B 类发生冲突,而在 B 类中有单独的代码用于与 A 类发生冲突,那么您可能会无意中使它们不一致。 (也许在你的游戏中只有蛇可以与物体发生碰撞,所以这在实践中不会发生,但在更一般的情况下,这将是一个值得考虑的风险。)
  • 我不明白你的意思;一切都处理自己与其他一切的冲突。 A 只在与 B 发生冲突时处理它自己的结束,而 B 只在与 A 发生冲突时处理它自己的结束。我不确定我理解你所说的“不一致”是什么意思
  • 如果你犯了一个错误(例如,你决定改变 A/B 冲突的某些方面并且你在 A 类中改变它而不是在 B 类中改变它),那将是不一致的。显然,如果你从不犯错误,那么如何组织代码并不重要,但对于像我这样容易犯错的凡人来说,将相关代码保持在一起会有所帮助。
【解决方案2】:

IMO 让游戏类完全处理碰撞和其他全局事件的最后一种方法是 IMO 最明智的做法,也可能是最“正确”的做法。
以国际象棋程序为例。很明显,游戏类应该处理其中一名玩家的输赢情况,而不是玩家类。
它在实践中也更有效。考虑一下您想要添加更多类(例如另一种类型的蛇)的情况,而不是复制代码或覆盖 CollisionHandler,您可以让游戏类更轻松地处理它。

但是,如果您的唯一目的是一个简单的蛇游戏,那么您选择如何实现它可能并不那么重要。

【讨论】:

  • 如果我再添加另一种Snake类,使用双调度方法不是更简单吗?无需重复代码,而且可以很好地分成模块。
  • 哎呀。单行通用 CollisionHandler(WorldObject&) 函数存在少量代码重复
  • 如果您将从 WorldObject 继承,您将需要复制代码。另外,作为一个经验法则,最好不要在不必要的时候使用函数重载。
猜你喜欢
  • 1970-01-01
  • 2011-07-06
  • 2013-07-25
  • 1970-01-01
  • 2018-05-26
  • 2017-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多