如何在基于组件的游戏中更新实体状态和动画?


10

我正在尝试设计一种基于组件的实体系统,以供学习(以及以后在某些游戏中使用),并且在更新实体状态时遇到了一些麻烦。

我不想在组件内部使用update()方法来防止组件之间的依赖关系。

我目前要记住的是,组件保存数据,而系统更新组件。

因此,如果我有一个简单的2D游戏,其中包含一些具有“变形”,“运动”,“状态”,“动画”和“渲染”组件的实体(例如玩家,敌人1,敌人2),我认为我应该拥有:

  • 一个MovementSystem,用于移动所有Movement组件并更新State组件
  • 还有一个RenderSystem,用于为每个状态更新Animation组件(animation组件应具有一个动画(即一组帧/纹理),并对其进行更新意味着选择与当前状态相对应的动画(例如,jumping,moving_left等),以及更新框架索引)。然后,RenderSystem使用与每个实体的Animation的当前帧相对应的纹理更新Render组件,并将所有内容呈现在屏幕上。

我已经看到了一些像Artemis框架的实现,但是我不知道如何解决这种情况:

假设我的游戏包含以下实体。每个实体都有一组状态和每个状态一个动画:

  • 播放器:“ idle”,“ moving_right”,“ jumping”
  • 敌人1:“上移”,“下移”
  • 敌人2:“向左移动”,“向右移动”

为了更新每个实体的当前状态,最常用的方法是什么?我唯一能想到的就是为每个实体组分别具有单独的系统以及单独的State和Animation组件,因此我将拥有PlayerState,PlayerAnimation,Enemy1State,Enemy1Animation ... PlayerMovementSystem,PlayerRenderingSystem ...但我认为这很不好解决方案并打破了拥有基于组件的系统的目的。

如您所见,我在这里很迷路,因此非常感谢您的帮助。

编辑:我认为使这项工作如我所愿的解决方案是:

使statecomponent和animationcomponent具有足够的通用性,可用于所有实体。它们包含的数据将成为修改诸如播放动画或可用状态之类的修改器。–字节56

现在,我试图弄清楚如何设计这两个通用组件,以便我可以重用它们。可以为每个状态(例如,步行,跑步...)设置UID并将动画存储在地图中并由该标识符作为键的AnimationComponent中吗?


我假设您已经看到以下内容:状态实体或组件中的更改?您的问题与该问题根本不同吗?
MichaelHouse

@ Byte56是的,我几个小时前读过。您在此处建议的解决方案与我在此处介绍的想法类似。但是,当系统中的所有实体的StateComponent和AnimationComponent不相同时,就会出现我的问题。我是否应该将该系统拆分为较小的系统,以处理具有相同可能状态和动画的实体组?(有关详细说明,请参阅我的原始帖子的
后半部分

1
您制作statecomponentanimationcomponent足以用于所有实体。它们包含的数据将成为修改诸如播放动画或可用状态之类的修改器。
MichaelHouse

当谈到依赖时,您是指数据依赖还是执行顺序依赖?另外,在您提出的解决方案中,MovementSystem现在必须实现事物可以移动的所有不同方式?看来这正在打破基于组件的系统的想法……
亚洲开发银行

@ADB我在谈论数据依赖。为了更新动画(例如,从move_right动画更改为move_left动画),我需要了解实体的当前状态,并且看不到如何使这两个组件更加通用。
miviclin '10 -10-26

Answers:


5

恕我直言,Movement组件应保持当前状态(Movement.state),并且Animation组件应观察状态的变化Movement.state并相应地更新其当前动画(Animation.animation),方法是使用简单的状态ID查找动画(如OP末尾所建议的)。显然,这Animation将取决于Movement

一种替代结构将是具有一个通用State组件,用于Animation观察和Movement修改,该组件基本上是模型视图控制器(在这种情况下为状态动画移动)。

另一种选择是让实体在状态更改时向其组件调度事件。Animation将收听此事件并相应地更新其动画。尽管您可以说依赖版本是更透明的设计,但是这消除了依赖。

祝好运。


因此,Animation观察状态,State观察运动...依存关系仍然存在,但我可以尝试一下。最后一种选择是否会是这样:运动通知实体更改,并且实体向State调度一个事件,然后对State和Animation重复相同的过程?这种方法如何影响性能?
miviclin

第一种情况:Movement控制 State(不观察)。最后一种情况:是的,大约Movement会这样做entity.dispatchEvent(...);,并且侦听此类事件的所有其他组件都将收到该事件。性能当然比纯方法调用差,但并不太多。例如,您可以合并事件对象。顺便说一句,您不必将实体用作“事件节点”,也可以使用专用的“事件总线”,从而将实体类完全排除在外。
Torious

2

关于您的问题,如果仅在动画中使用STATE,那么您甚至不需要将其公开给其他组件。如果它有多种用途,则需要公开它。

您所描述的组件/子系统系统比基于组件的感觉更基于层次结构。毕竟,您描述为组件的实际上是数据结构。这并不意味着它是一个糟糕的系统,只是我认为它不太适合基于组件的方法。

如您所述,依赖关系在基于组件的系统中是一个大问题。有不同的处理方法。有些要求每个组件声明其依赖关系并进行严格检查。其他人则查询实现特定接口的组件。还有一些实例化每个依赖组件时会引用它们。

独立于所使用的方法,您将需要某种GameObject来充当Components的集合。GameObject提供的功能变化很大,您可以通过将一些常用数据推送到GameObject级别来简化组件间的依赖性。例如,Unity通过转换来做到这一点,迫使所有游戏对象都拥有一个。

关于您要求针对不同游戏对象的不同状态/动画的问题,这就是要做的。首先,在实现的这个阶段我不会太花哨:仅实现您现在需要的东西以使其正常工作,然后在需要时添加铃铛。

因此,我将从“状态”组件开始:PlayerStateComponent,Enemy1State,Enemy2State。状态组件将负责在适当的时间更改状态。状态几乎是所有对象都会拥有的东西,因此它可以驻留在GameObject中。

然后,将有一个AnimationCompoment。这将有一个键控该状态的动画字典。在update()中,如果状态更改,则更改动画。

关于构建框架,有一篇很棒的文章我找不到。它说,如果您没有相关领域的经验,则应该选择一个问题,并执行能够解决当前问题的最简单的实现。然后,添加另一个问题/用例,并在框架上进行扩展,使其自然增长。我真的很喜欢这种方法,尤其是当您在使用新概念时。

我提出的实现非常幼稚,但是随着您添加更多用例,以下是一些可能的改进:

  • 用字典替换GameObject变量。每个组件都使用字典来存储值。(确保正确处理碰撞...)
  • 用引用替换普通值字典:class FloatVariable(){公共值[...]}
  • 创建通用的StateComponent,而不是多个状态组件,可以在其中构建变量状态机。您需要具有一组可以更改状态的通用条件:按键,鼠标输入,变量更改(您可以将其绑定到上面的FloatVariable)。

这种方法行得通,一年前我实现了类似的方法,但是问题在于几乎每个组件都依赖于其他组件,因此对我来说似乎不太灵活。我还考虑过将最常见的组件(例如,变换,渲染,状态...)推入实体,但是我认为这破坏了组件的用途,因为其中一些组件与实体绑定在一起,而某些实体可能不需要它们。这就是为什么我要使用负责更新逻辑的系统来重新设计它,以便组件之间不会相互了解,因为它们不会自行更新。
miviclin

0

除了ADB的答案外,您还可以使用http://en.wikipedia.org/wiki/Dependency_injection,当您需要通过将它们作为对构造函数的引用传递来构造许多组件时,它可以提供帮助。显然,它们仍将相互依赖(如果在代码库中是必需的),但是您可以将所有依赖项放在一个位置,在该位置设置了依赖项,而其余代码无需了解依赖项。

如果使用接口,此方法也很好用,因为每个组件类仅要求其需要或需要在哪里注册,并且只有依赖项注入框架(或设置所有内容的位置,通常是应用程序)才知道谁需要什么。 。

对于简单的系统,您可能无需使用DI或干净的代码就无法使用,RenderingSystem类听起来像您需要静态调用它们,或者至少在每个组件中都提供它们,这几乎使它们彼此依赖并且难以更改。如果您对更清洁的方法感兴趣,请查看上面的DI Wiki链接,并阅读有关清洁代码的信息:http : //clean-code-developer.com/


我已经有一个系统,组件之间非常相互依赖。我在那里大量使用了依赖注入,尽管我比深度层次结构更喜欢它,但我正在尝试创建一个新的方法,以尽可能避免组件耦合。我不会静态调用任何东西。我将拥有一个ComponentManager,每个系统都可以访问(每个系统都应具有对它的引用),并且RendererSystem将从组件管理器中获取所有动画组件,并呈现每个动画的当前状态。
miviclin '10 -10-27
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.