OOP设计播放器和AI控制单元。 dynamic_cast的?

时间:2014-08-11 22:01:43

标签: c++

假设我有一个Actor类型,它可以是我游戏关卡中的任何可放置对象。 我也有单位,这是一个儿童类的演员。单位可以是玩家或AI控制的英雄。

Unit还有2个子类:玩家(玩家)和英雄(AI控制单位)。 在Unit类中,将有Player和Hero都需要的移动信息,旋转和其他常规设置。在子类中,AI将具有与玩家不同的功能。

现在我面临以下问题:

一个函数只接受Actor作为参数(例如OnOverlap(Actor a*)) 但是OnOverlap()只应该做一些事情,如果它是一个单元类(英雄或玩家)。 因此,我需要在C ++中使用Java中的instanceof()

解决方法是使用dynamic_cast,但我不确定这是否对性能有好处。或者使用虚拟,但是当Hero比Player有更多功能时,这不会起作用。

或者我应该尝试全新的OOP设计?

4 个答案:

答案 0 :(得分:2)

我说dynamic_castcode smell。本质上没有必要,但表明您的设计可能出现问题。

OOP中的一个重要概念是多态性:对象根据其类型行为不同的想法,以及此行为是封装,隐藏在接口后面。如果你明确地检查一个对象的类型来改变你想要应用它的逻辑,那么你就是在违反多态。

现在虚拟方法也不是很好,它们确实会产生运行时成本,有时会给表带来太多的复杂性。 C ++使virtual方法成为异常,而非默认,我相信这些原因。

您应该知道的一件事是,虚方法只是一种多态,称为动态多态。另一种获取不同行为的方法取决于对运行时较少阻碍的类型:静态多态,AKA template metaprogramming。但这对复杂性方面并没有帮助。

现在你在这种情况下应该做的是分开处理单独的事情。您可能希望在OnOverlap()上使用此Units方法是有原因的:让我们说您仅对Units进行碰撞检查,而不是全部Actors }。然后维护一个单独的Units列表,并在OnOverlap()类上使该Unit方法非虚拟。以这种方式思考往往是关键。

答案 1 :(得分:0)

此技术称为Run-Time Type Information (RTTI),并检查dynamic_cast的输出是否为NULL是有效的实现。

另一种方法是#include <typeinfo>并使用typeid(*a).name()来获取类型的字符串名称。

答案 2 :(得分:0)

使用dynamic_cast的需要经常(尽管并非总是)表明您存在设计缺陷。在这种情况下,我认为你这样做。

如果没有进一步评论您所描述的架构的整体设计,我会说使用虚拟方法是正确的方法。让OnOverlap接受它的演员并在actor上调用OnOverlapped方法,也许给它一个指向执行初始OnOverlap方法的东西的指针(它不是从你的问题中清楚那是什么(*))。

void X::OnOverlap (Actor * actor) {
  actor->OnOverlapped(this);
}

OnOverlapped本身就是虚拟的。这可以让你做正确的事情&#34;在演员单元的情况下,但在其他地方什么也不做或其他一些默认行为。你是对的,在这种情况下,这意味着你只能使用Actor的公共API。您必须将需要Player其他方法的任何内容移至Player OnOverlapped的{​​{1}}实现中。如果你真的认为你出于某些原因不能这样做,你或许应该考虑更高层次的替代设计(可能需要编辑你的问题以提供有关你的架构的更多细节)。

(*)我对您的问题的初步阅读让我觉得OnOverlap已经不是Actor的方法了;它是不相关的东西的一部分。如果 Actor的一部分,您可能会对名为double dispatch的模式感兴趣,该模式可用于此类&#34;碰撞处理&#34;问题的类型,也是一种使用动态调度(虚拟)的技术。

答案 3 :(得分:0)

  

这些功能由我的引擎预定义,参数必须在此处   但是父函数“Actor”。所以如果任何一个Actor与之相撞   网格,该函数被调用。在这种情况下,我没有看到别的   解决方案,而不是让Actor对象的实例知道它是否   只是其他东西的玩家

Actor基类中的一个简单虚函数可以是一个noop。在Unit类中重写它,如果需要,在Hero和Player中进一步专门化它。非单位演员将调用一个空方法。比RTTI和惯用语更清洁。

可以使用一个默认的基本实现,它希望被专门的后代覆盖。这是抽象基类的重点,除非在这种情况下,您提供的默认实现不执行任何操作。这个想法是你要发送“DoCollision”消息(C ++中的虚函数)。您不关心哪些类处理它或如何处理它。只是该对象具有DoCollision编译时“接口”并且您正确地发送了它。