我目前正在了解"实体组件系统"。 在阅读了许多教程和论坛帖子后,我仍然想知道渲染逻辑必须去哪里。我不是在谈论实际的OpenGL / DirectX渲染代码,例如,它采用精灵并渲染它。我的意思是决定要渲染哪个精灵的逻辑。
可见实体需要三个方面:
大多数教程建议使用类似AISystem(1.)的逻辑和RenderSystem(3。)来显示在RenderComponent中定义的sprite(循环)。他们没有说的是其中 RenderComponent的更新。 我的理解是,将(2.)放入(1.),从而将字符逻辑与渲染逻辑混合,将是糟糕的设计。
直接的解决方案是为每个敌人添加一个特定的渲染逻辑系统。因此,例如对于Gumba,我可以添加GumbaLogicSystem,GumbaRenderLogicSystem以及用于实际渲染的基于所有基于sprite的实体使用的通用SpriteRenderSystem。但是,这意味着为每个实体类型创建两个组件*和两个系统,这似乎不是一个好的解决方案。
是否有一个很好的解决方案可以将字符逻辑和渲染逻辑分开,同时保持较小的系统数量?
谢谢
(* =在一个简单的方法中,系统根据其组件处理实体。为了让GumbaRenderLogicSystem在实体上工作,它需要一个GumbaRenderingLogicComponent才能被这个系统识别。同样的情况也是如此。字符逻辑。)
编辑1:我知道ECS是一种抽象模式。我的问题针对如何保持系统数量较少的最佳实践。我的例子是从游戏编程推动的,但不限于这个领域。让我用一个更抽象的例子来解释:
想象一下,我有一些可见的实体。在基于层次结构的架构中,我会有类似的东西:
在ECS中,我需要一大堆新课程:
有什么方法可以减少需要引入的新系统的数量?一种解决方案可能是添加"代理商"或者"行为对象":一般的RenderingComponent附加了一些" RenderingAgent"的实例。具有单一方法的类"更新"在RenderSystem中调用。因此从技术上讲,组件本身不包含逻辑。我担心这可能会过度工程化。
答案 0 :(得分:0)
实体组件系统是一种模式,即解决游戏代码中的复杂性问题的一种方式。作为一种“模式”,它只描述方法,并保持相当抽象。因此没有明确的提及。
所以,当你明确地逐个调用阶段时,让我们考虑使用传统的循环。
while(true)
{
callAI(); // AI entities
checkCollisions(); // some physics here
updatePositions(); // movable objects
render(); // visual elements.
}
使用ECS,您将拥有两个循环,每个子系统的外部,内部 - 适用于每个合适的组件。
var types = {'ai', 'mechanic', 'visual'};
while(true)
{
for(var t in types)
{
var handler = getHandlerForType(t);
for(var e in entities)
{
var c = e.getComponents(t); // receive e's components of required type
// So this would handle ai, mechanics or visual component respectfully.
handler.handle(c);
}
}
}
(这些循环,虽然它们说明了一般方法,但远非完美的实现。实际上你可能会获取所有组件,然后分批处理它们。)
所以你不再有明确的循环阶段了。相反,渲染成为“子系统”之一,它只处理那些能够自我渲染的组件。从理论上讲,你可以在任何地方拥有这个部分,但可能更有意义的是将它放在那个管道的最后。
答案 1 :(得分:0)
经过一段时间的思考和许多讨论,我意识到,我的思维方式可能是错误的。我所描述的实际上对于香草ECS方法是正确的。
防止系统和组件爆炸的唯一方法是为您的游戏元素提供适当的抽象,这样就可以描述描述的各种需求,而不是被编程。
我的意思是,例如,精灵引擎必须允许足够的抽象,您只能通过存储在渲染系统中评估的渲染组件中的描述来表达各种动画和状态。您还需要做的是将您的解决方案正确地拆分为可重用的部分。
通过这种方式,ECS可以比其他模式更强调您真正考虑好的架构。