我将基于一些经验,从严格的OO设计到实体组件系统(ECS)设计进行演讲。
前一阵子我就像你一样,我有一堆具有相似属性的不同类型的东西,我建立了各种对象并尝试使用继承来解决它。一个非常聪明的人告诉我不要这样做,而应使用Entity-Component-System。
现在,ECS是一个很大的概念,很难做到正确。要做很多工作,正确地构建实体,组件和系统。但是,在执行此操作之前,我们需要定义术语。
- 实体:这是事物,玩家,动物,NPC,无论如何。这是需要附加组件的东西。
- 组件:这是属性或属性,例如您的情况下的“名称”或“年龄”或“父母”。
- 系统:这是组件或行为背后的逻辑。通常,您为每个组件构建一个系统,但这并不总是可能的。此外,有时系统需要影响其他系统。
所以,这就是我要去的地方:
首先,ID
为您的角色创建一个。An int
,Guid
无论您喜欢什么。这是“实体”。
其次,开始考虑您正在进行的不同行为。像“家谱”之类的东西-这是一种行为。建立一个包含所有信息的系统,而不是将其建模为实体上的属性。然后,系统可以决定如何处理。
同样,我们要为“角色是活着还是死了?”构建一个系统。这是设计中最重要的系统之一,因为它会影响所有其他系统。一些系统可以删除“死”字符(例如“ sprite”系统),其他系统可以在内部重新排列事物以更好地支持新状态。
例如,您将构建一个“ Sprite”或“ Drawing”或“ Rendering”系统。该系统将负责确定需要与哪个角色一起显示角色,以及如何显示角色。然后,当角色死亡时,将其删除。
另外,一个“ AI”系统可以告诉角色该做什么,该去哪里,等等。这应该与许多其他系统交互,并根据它们做出决策。同样,死角字符可能可以从该系统中删除,因为它们实际上不再做任何事情了。
您的“名称”系统和“家谱”系统应该将字符(有效或无效)保留在内存中。无论角色的状态如何,该系统都需要重新调用该信息。(即使我们将他埋葬了,吉姆仍然是吉姆。)
这还为您带来了改变系统更有效响应时间的好处:系统具有自己的计时器。有些系统需要快速启动,而有些则不需要。这是我们开始研究使游戏有效运行的原因。我们不需要每毫秒重新计算一次天气,我们大概每5秒钟可以进行一次。
它还可以为您提供更多的创造力:您可以构建一个“路径查找器”系统,该系统可以处理从A到B的路径的计算,并且可以根据需要进行更新,从而使Movement系统可以说“我需要在哪里下一个?” 现在,我们可以将这些问题完全分开,并更有效地进行推理。运动不需要找到道路,只需要让您到达那里即可。
您将需要将系统的某些部分暴露给外部。在您的Pathfinder
系统中,您可能需要一个Vector2 NextPosition(int entity)
。这样,您可以将这些元素保留在严格控制的数组或列表中。您可以使用更小,struct
类型,它可以帮助你保持部件更小,连续的内存块,它可以使系统更新多快。(特别是如果对系统的外部影响很小,那么现在只需关心它的内部状态,例如Name
。)
但是,我对此并不足够强调,现在一个Entity
仅仅是一个ID
,包括图块,对象等。如果实体不属于系统,则系统将不会对其进行跟踪。这意味着我们可以创建“树”对象,将它们存储在Sprite
和Movement
系统中(树不会移动,但是它们具有“位置”组件),并将其与其他系统隔离。我们不再需要特殊的树列表,因为除了纸质文字外,渲染树与字符没有什么不同。(这是Sprite
系统可以控制的,还是Paperdoll
系统可以控制的。)现在NextPosition
可以稍微重写Vector2? NextPosition(int entity)
一下:,它可以null
为无关的实体返回位置。我们也将其应用于我们NameSystem.GetName(int entity)
,返回null
树木和岩石。
我将对此进行总结,但是这里的想法是为您提供有关ECS的背景知识,以及如何真正利用它来为您的游戏提供更好的设计。您可以提高性能,分离不相关的元素,并使事情保持井井有条。(这也与功能语言/设置(例如F#和LINQ )配合得很好,如果您还没有的话,我强烈建议您检查F #;当您结合使用它们时,它与C#会很好地搭配。)