组成重的OOP与纯实体组件系统?[关闭]


13

我承认,我犯了过度使用甚至滥用继承的罪过。我上OOP课程时所做的第一个(文字)游戏项目远比“门”和“一扇门的房间”,“两扇门的房间”中的“锁门”和“解锁门”有关,以此类推。

通过最近我从事的(图形)游戏,我认为自己已经学到了教训,并限制了继承的使用。但是我注意到问题很快开始出现。我的根类开始越来越膨胀,而我的叶类中充满了重复的代码。

我以为我还是在做错事,在网上查询之后,我发现我并不是唯一遇到此问题的人。经过一些深入的研究,我最终才发现了实体系统(请参阅:googlefu)

当我开始阅读它时,我能够看到它能够多么清晰地解决我的带有组件的传统OOP层次结构所遇到的问题。这些是一读。当我偶然发现更多……“激进”的ES方法时,例如T-machine上的方法

我开始不同意他们使用的方法。一个纯粹的组件系统似乎要么过大,要么不直观,这可能是OOP的优势。作者甚至说ES系统与OOP相反,尽管它可以在OOP中使用,但实际上不应该。我并不是说这是错误的,但是我只是觉得自己不想实施一个解决方案。

因此,对我来说,为了解决我在帖子开头所遇到的问题,而又不违背我的直觉,那就是仍然使用层次结构,但是它不会像我以前使用的那样是一个整体的层次结构,而是一种多片的(我找不到与单片相反的单词),它由几棵较小的树组成。

下面的示例说明了我的意思(这受我在《游戏引擎体系结构》第14章中找到的示例的启发)。

我会有一棵小树供车辆。根车辆类别将具有渲染组件,碰撞组件,位置组件等。

然后,坦克(车辆的子类)会从中继承这些组件,并被赋予自己的“加农炮”组件。

角色也是如此。角色将拥有自己的组件,然后让Player类继承它,并被赋予Input控制器,而其他敌人类将从Character类继承并被赋予AI控制器。

我真的看不到这种设计有任何问题。尽管没有使用纯的实体控制器系统,但是冒泡效果的问题和大根类的问题是通过使用多树层次结构解决的,繁重的代码复制叶子的问题也消失了,因为叶子没有有任何代码开始,只有组件。如果需要对叶级别进行更改,则就像更改单个组件一样简单,而不是将代码复制粘贴到任何地方。

当然,像我一样没有经验的人,当我刚开始使用单一层次结构,繁重的继承模型时,我没有看到任何问题,因此,如果我当前正在考虑实现的模型存在问题,我不会能够看到它。

您的意见?

PS:我正在使用Java,因此无法使用多重继承来实现此功能,而不是使用常规组件。

PPS:组件间通信将通过相互链接相关的组件来完成。这将导致耦合,但是我认为这是一个不错的权衡。


2
在我看来,这一切都很好。您的层次结构或实体系统中是否有一个特定的示例对您“有异味”?最后,完成游戏绝不仅仅是纯粹的感觉。
drhayes 2012年

我没有,但是就像我说的那样,我几乎没有任何经验,而且我离最佳法官还很远。
Midori Ryuu 2012年

3
我投票结束这个问题,因为它是主观的并且可以进行广泛讨论。我很高兴从一个真诚的立场上提出这个问题-但在这里选择如何进行完全取决于个人观点。将组件固定到子类中的唯一真正的缺点是,组件的排列在运行时不可更改-但这肯定对您很明显,这是您做出的选择。
Kylotan '08年

4
我也投票决定关闭。这是一个写得很好的好问题,但不适用于GDSE。您可以尝试将其重新发布到GameDev.net。
肖恩·米德迪奇

如所写,问题是“您的意见吗?”,这的确是开放性的,无法回答。但是,如果您回答的问题好像是“我是否会在没有注意到的情况下陷入任何巨大的陷阱?” ,这可以回答的。(至少,如果您认为各种架构之间实际上存在任何经验差异。)
John Calsbeek 2012年

Answers:


8

考虑以下示例:

您正在制作RTS。在一个合适的完整的逻辑,你决定做一个基类GameObject,然后两个子类,BuildingUnit。当然,这工作得很好,最终您会得到如下所示的内容:

  • GameObject
    • ModelComponent
    • CollisionComponent
  • Building
    • ProductionComponent
  • Unit
    • AimingAIComponent
    • WeaponComponent
    • ParticleEmitterComponent
    • AnimationComponent

现在,Unit您制作的每个子类已经具有所需的所有组件。另外,您可以将所有繁琐的设置代码new放在一个位置WeaponComponent,并将它连接到AimingAIComponent和,这ParticleEmitterComponent真是太好了!

当然,由于它仍然将逻辑因素纳入组件中,因此当您最终决定要添加Tower一个建筑物但有武器的类时,就可以对其进行管理。您可以添加一个AimingAIComponent,一个WeaponComponent,和ParticleEmitterComponent你的子类Building。当然,然后您必须仔细研究并从Unit类中挖掘出初始化代码,然后将其放在其他位置,但这并没有什么大的损失。

现在,根据您的远见卓识,您可能会或可能不会遇到细微的错误。事实证明,游戏中的其他地方可能有一些代码如下:

if (myGameObject instanceof Unit)
    doSomething(((Unit)myGameObject).getWeaponComponent());

Tower即使您确实希望它可以用武器用于任何事物,这对于您的建筑物而言都是无声的。现在它必须看起来像这样:

WeaponComponent weapon = myGameObject.getComponent(WeaponComponent.class);
if (weapon)
    doSomething(weapon);

好多了!更改此设置后,您可能会想到,您编写的任何访问器方法都可能在这种情况下结束。因此,最安全的方法是将它们全部剥离,并通过此方法访问每个组件,该getComponent方法可与任何子类一起使用。(或者,您可以将每一种添加get*Component()到超类中GameObject,并在子类中重写它们以返回组件。不过,我假设第一个。)

现在,您BuildingUnit类几乎只是一堆组件,甚至没有访问器。而且也没有花哨的初始化,因为大部分都需要拖出以避免代码重复与某个特殊情况下的其他对象在某个地方进行。

如果将其严格地遵循,那么最终总会使子类完全惰性化,因此在任何时候都没有子类的存在。使用具有创建适当组件的方法层次结构的类层次结构,您几乎可以获得所有好处。除了拥有一个Unit类之外,您还有一个addUnitComponents使an AnimationComponent且还调用的方法addWeaponComponents使得an AimingAIComponent,a WeaponComponent和a ParticleEmitterComponent。结果是您拥有实体系统的所有优点,层次结构的所有代码重用,并且没有任何诱惑可以检查instanceof或强制转换为类类型。


说得好。除此之外,我认为理想的解决方案是使用脚本或描述符文件初始化每个实体。我使用文本文件进行实体初始化。它速度很快,并且允许非程序员测试不同的参数。
郊狼2012年

哇,阅读您的帖子给了我一个噩梦!当然,我的意思是,以一种很好的方式,看到您睁开眼睛,我可能会遇到什么样的噩梦!我希望我能对如此详细的答案做出更多答复,但实际上没有太多补充!
Midori Ryuu 2012年

以更简单的方式。您最终将继承用作穷人的工厂模式。
Zardoz89 '16

2

继承之上的组合是OOP术语(至少是我学习/教导它的方式)。要在合成和继承之间进行选择,请大声说出“汽车是一种轮子”(继承)和“汽车有轮子”(组成)。一个听起来应该比另一个更正确(在这种情况下为合成),如果您不能决定,则默认为合成,除非您另行决定。


1

在将基于组件的系统集成到基于游戏对象的现有游戏时,有关牛仔编程的文章为http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/,这可能会为您提供一些指导。过渡的方式。

至于问题,您的模型仍然需要以不同于其他敌人的方式处理玩家。当玩家断开连接或在特殊情况下无法与其他角色区别对待时,您将无法切换状态(即玩家变为AI控制)。

除了在不同实体之间大量重用组件之外,基于组件的游戏的思想是整个系统的统一性。每个实体都有可以随意连接和分离的组件。通过每种类型的接口(基础组件)建立的一组调用(位置,渲染,脚本...),完全相同地处理所有相同类型的组件。

将游戏对象继承与基于组件的方法混合在一起会为您带来基于组件的方法的一些优点,但同时也带来了两种方法的缺点。

它可以为您服务。但是我不会在从一种架构过渡到另一种架构的过渡时期内将两者混合使用。

PS写在手机上,因此我很难链接到外部资源。


即使您正在打电话,也感谢您的回复!我明白你的意思。基于移动组件的事物,我必须具有更大的灵活性来改变事物。这就是为什么我要继续尝试最小继承的组合方法的原因。(甚至比我建议的还要多。)
Midori Ryuu 2012年

@MidoriRyuu避免实体对象中的所有继承。您很幸运地使用了内置反射(和自省)的语言。您可以使用文件(txt或XML)使用正确的参数来初始化对象。这不是太多的工作,并且无需重新编译即可增强游戏的微调。但是至少应保留Entity类,以用于组件间通信和其他实用程序任务。PS仍在电话上:(
郊狼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.