首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >复合模式/实体系统和传统的OOP

复合模式/实体系统和传统的OOP
EN

Stack Overflow用户
提问于 2011-02-09 14:30:10
回答 3查看 8.3K关注 0票数 18

我正在开发一个用Java编写的小游戏(但这个问题与语言无关)。因为我想探索各种设计模式,所以我被Composite pattern/Entity system (我最初读到的关于herehere)作为典型的深层次继承的替代方案所吸引。

现在,在写了几千行代码之后,我有点困惑了。我认为我理解这个模式,而且我喜欢使用它。我认为它很酷,有点像星巴克,但它觉得它提供的好处是短暂的,(最让我恼火的是)严重依赖于你的粒度。

这是上面第二篇文章中的一张图片:

我喜欢对象(游戏实体,或者你想叫它们的任何东西)具有最小组件集的方式,由此推断出的想法是,你可以编写类似于以下内容的代码:

代码语言:javascript
复制
BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new Movement(), new Render(), new Script(), new Target());
Player.addComponent(new Position(), new Movement(), new Render(), new Script(), new Physics());

。。那就太好了..。但实际上,代码最终看起来如下所示

代码语言:javascript
复制
BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());
Player.addComponent(new Position(), new KeyboardInputMovement(), new RenderPlayer(), new ScriptPlayer(), new PhysicsPlayer());

似乎我最终拥有了一些非常专业的组件,这些组件由较少的组件组成。很多时候,我不得不制作一些依赖于其他组件的组件。毕竟,如果你没有位置,你怎么渲染呢?不仅如此,你最终渲染玩家、外星人和手榴弹的方式也可能是完全不同的。你不能用一个组件来指定所有三个组件,除非你做了一个非常大的组件(在这种情况下...你为什么要使用复合模式呢?)

再举一个真实的例子。我的游戏中有可以装备各种装备的角色。当装备一个齿轮时,一些统计数据以及视觉上显示的内容都会发生变化。下面是我的代码现在的样子:

代码语言:javascript
复制
billy.addControllers(new Movement(), new Position(), new CharacterAnimationRender(), new KeyboardCharacterInput());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterAnimationRender.class).setFace(FACE.BLUSH_FACE);
billy.get(CharacterAnimationRender.class).setHair(HAIR.RED_HAIR);
billy.get(CharacterAnimationRender.class).setDress(DRESS.DRAGON_PLATE_ARMOR);

上面的CharacterAnimationRender.class只影响视觉上显示的内容。因此,我显然需要制作另一个处理齿轮统计数据的组件。但是,我为什么要这样做:

代码语言:javascript
复制
billy.addControllers(new CharacterStatistics());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterStatistics.class).setBodyStats(BODY_STATS.NORMAL_BODY);

什么时候我可以只制作一个CharacterGearStuff控制器/组件,既处理统计信息的分布,又处理可视化的更改?

归根结底,我不确定这是如何帮助提高生产率的,因为除非你想手动处理所有事情,否则你仍然必须创建依赖于2+组件的“元组件”(并修改/交叉修改它们的所有子组件-将我们带回到OOP)。也许我的想法完全错了。我是吗?

EN

回答 3

Stack Overflow用户

发布于 2011-02-09 16:20:38

大卫,

首先,感谢你完美的问题。

我理解您的问题,并认为您没有正确使用该模式。请阅读本文:http://en.wikipedia.org/wiki/Composite_pattern

例如,如果你不能实现一般的类移动,并且需要AlienAIMovement和KeyboardMovement,那么你可能应该使用访问者模式。但是,在开始重构数千个代码行之前,请检查是否可以执行以下操作。

是否有机会编写接受BaseEntity类型参数的类移动?也许移动的所有实现之间的区别只是一个参数、标志或其他?在这种情况下,您的代码将如下所示:

Alien.addComponent(new Position(), new Movement(Alien), new Render(Alien), new Script(Alien), new Target());

我认为这并不是那么糟糕。

如果不可能,请尝试使用工厂创建实例,因此

Alien.addComponent(f.createPosition(), f.createMovement(Alien), f.createRender(Alien), f.createRenderScript(Alien), f.createTarget());

我希望我的建议能有所帮助。

票数 2
EN

Stack Overflow用户

发布于 2013-11-16 22:47:15

Ents似乎被设计成能做你想做的事情。如果你仍然想要自己的库,你至少可以从它的设计中学习。

之前的答案看起来都很笨拙,并创建了大量不必要的对象,IMO。

票数 2
EN

Stack Overflow用户

发布于 2011-02-09 16:19:12

我认为你在这里使用了错误的方法。模式应该被用来满足你的需求,而不是相反。简单总是比复杂好,如果你觉得有什么不对劲,这意味着你应该后退几步,也许从头开始。

对我来说,这已经有了一种代码的味道:

代码语言:javascript
复制
BaseEntity Alien = new BaseEntity();
Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());

我希望面向对象的代码看起来像这样:

代码语言:javascript
复制
Alien alien = new AlienBuilder()
    .withPosition(10, 248)
    .withTargetStrategy(TargetStrategy.CLOSEST)
    .build();

//somewhere in the main loop
Renderer renderer = getRenderer();
renderer.render(alien);

当您为所有实体使用泛型类时,您将拥有一个非常通用且难以使用的API来处理您的对象。

此外,将位置、移动和渲染器放在同一组件下是错误的。位置不是一个组件,它是一个属性。移动是一种行为,渲染器是与你的领域模型无关的东西,它是图形子系统的一部分。零件可能是汽车的轮子,身体的零件和外星人的枪。

游戏开发是一件非常复杂的事情,很难从一开始就让它变得正确。从头开始重写你的代码,从你的错误中学习,感受你正在做的事情,而不仅仅是试图从一篇文章中定制一个模式。如果你想在模式上变得更好,你应该尝试比游戏开发更好的东西。例如,编写一个简单的文本编辑器。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4941953

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档