2011-08-02 207 views
3

我有一个关于如何处理这种类层次结构的问题。层次结构与游戏有关,但我认为它足以通过gamdev。我可以想象这种事偶尔会出现,所以我很好奇可能的解决方案如何处理类层次结构

我有一个名为BaseCharacter的基类,它定义了我的游戏中的一个角色。它包含所有基本统计数据,状态效应持有者,事件等以具有基线字符。我的大部分游戏逻辑都是在BaseCharacter的实体上进行操作,在必要时进行投射。

从那里我继承到一个人形生物类,它增加了必要的结构和方法来为角色添加装备(武器,护甲等),最后我有一个PlayableCharacter类,从类人生物吸入并添加了当前EXP,可配置能力结构等

因此类结构很简单:

BaseCharacter --->人形---> PlayableCharacter

这种结构用于定义玩家身边人物的伟大工程,但我可能会决定再改进一下结构。我必须能够将“敌人”类添加到层次结构中。为了满足敌人的需求,我需要添加一些与EXP /点有关的数据结构/方法来打败敌人,物品战斗表等。

问题是,我在哪里添加这些结构?将它们添加到BaseCharacter似乎是一个糟糕的主意,因为我基本上说每个角色都有一个敌方实体所需的方法/数据结构。我考虑开始一个单独的类,最终会看起来像这样:

BaseCharacter--->Humanoid--->PlayableCharacter 
      \---->Enemy--->EnemyHumanoid 

其中Enemy继承自BaseCharacter。 EnemyHumanoid将添加与BaseCharacter-> Humanoid的继承相同的一组方法/数据结构。这是有效的,但意味着我不能将敌人人型投入人型生物,尽管敌人人型符合人形生物的要求。这也增加了冗余,因为我有这两个继承添加完全相同的一组对象/方法。

然后使用名为EnemyInterface一个单独的类考虑,并与继承层次是这样结束:

BaseCharacter----->Humanoid----->PlayableCharacter 
    |     |     
    V     V     
    Enemy   EnemyHumanoid 

敌人EnemyHumanoid都实现EnemyInterface。但是,我现在失去了敌人< - >敌人人类关系。我应该在这里使用多重不义行为吗? (有敌人和人型生物的敌人人形?)被认为是坏形式?有没有更合乎逻辑的方法来做到这一点?这里的例子非常简单,但如果我最终分裂我的类层次更多,它只会变得更加复杂。

任何帮助,将不胜感激

回答

1

这听起来像你正在使用继承重用,而不是坚持LSP(http://en.wikipedia.org/wiki/Liskov_substitution_principle),这是通向地狱。

我的一般建议是只为is-a关系使用继承。也就是说,你可以将一种类型替换为另一种类型。对于大多数解决方案,我更喜欢组合来继承,因为我发现它更简单。

在您的具体情况下,为什么考虑敌人与PlayableCharacter不同?考虑在类阶层以外的地方使用EXP和Loot表,你可以将一个Humanoid传递给它并分别计算这些项目。

C++是一种多范式语言。您不必局限于OO解决方案。

祝你好运!

1

这可能是怎么回事?

BaseCharacter----->Humanoid----->PlayableCharacter 
        /\     
        / \     
       Enemy  EnemyHumanoid 

EnemyHumanoid可能只需要Humanoid的某些功能。

0

需要考虑的问题是Humanoid实例是否需要转换为BaseCharacter实例。你可能会发现它实际上没有。你真的需要迭代所有人形字符集吗?

如果是这样定义:

Humanoid 

BaseCharacter->PlayableCharacter (contains Humanoid) 
      \---->Enemy---->EnemyHumanoid (contains Humanoid) 

[你可能要考虑制定一个定义具体EnemyNonhumanoid敌人摘要]

另一种方法是定义人形作为接口,并提供getHumanoid( )方法在PlayableCharacter和EnemyHumanoid上实现,作为每种情况下的句柄。

2

而不是重新安排您的类层次结构来解决这个特定问题(请参阅StackUnderblow的答案以了解如何执行此操作)我建议您对基于组件的体系结构进行一些研究。基本上,不是有一个描述使PlayableCharacter独特的东西的类,而是将一个组件(或一组组件)嫁接到某种基本容器上。

这种方法要灵活得多,而且随着设计的发展将更容易重构。例如,如果你有一些特定的敌人和人类应该能够访问的特性,那么你必须将系统钩子添加到你的基类中,向不能使用该特征的敌人引入开销。这是描述一个简单问题的很多词语,但随着游戏的发展,你会一再地遇到问题。

Here是一个很好的SO讨论,扩展了我的建议。

Bheeshmar是对的,你是在地狱的路上!

1

你试图继承根据多个方面(错了,我不知道“纵横”在这里是正确的话,我不是以英语为母语,对不起),即你的BaseCharacter类:

  • 表 - 仿人/动物/等
  • 对齐 - 敌人/朋友/等

这总是会导致问题,因为你已经观察到:迟早你会想创建类它应该继承自BaseCharacter的多个子类(通常来自每个aspec的一个子类) t类),例如。人形+敌人。在这种情况下,您要么使用多重继承(由于实现问题,不推荐在C++中使用),要么需要重新设计代码。

通常最好的解决方案是根据前面指出的方面分割对象。例如。在一个BaseCharacter对象中,你可以有两个指针,一个CharacterForm *和一个CharacterAlignment *。然后你可以继承CharacterForm(如HumanoidForm,AnimalForm等)和CharacterAlignment(EnemyAlignment,FriendAlignment等)。),并根据需要进行组合。

请注意,这种方法确实是朝着基于组件的设计迈出的第一步,正如Dan O所建议的那样。

你需要做的每一次类似东西,你发现自己创建一个子类,这是它的基地在不同的感觉比以前的子类(如一个子类创建人形和动物,你需要敌人后 - >这显然是对BaseCharacters进行分类的另一种方式)。根据经验法则,每个类别只应根据一个方面而成为子类别。

0

这个怎么样?

BaseCharacter

IBehaviour是接口

HumanoidBehaviour被执行IBehaviour

BaseCharacter包含IBehaviour

Enemy继承BaseCharacter,并且包含HumanoidBehaviour

PlayableCharacter继承BaseCharacter,并且包含HumanoidBehaviour

所以在逻辑上我们会得到:Enemy是,一个BaseCharacter已经-A行为HumanoidBehaviour

可能Strategy design pattern将有助于在这里 也有funny book,有一个很好的例子)