Group 组合
对于ECS来说,一个典型的"Hello world"就是所谓的"move system"。move system,是一个获取所有拥有Position以及Velocity组件的Entity,并且更新他们的Position 信息,让他们有效的超目标Velocity移动的System。当我们意识到这个Entity上的其他Component并不重要时,自然就会明白这个道理。这个实体可以是人,狗,汽车,直升机或房子。但如果它有一个Position组件和一个Velocity组件,它必须移动。
那我们怎么获取这些实体呢?
如Context 章节中所述,Context管理着所有实体,因此我们可以向Context请求并遍历所有Entity,从而收集具有Position以及Velocity 的那些Entites。这将是一个非常幼稚(naive)的实现。针对这种情况下,我们在Entitas中应对措施就是Group。
context.GetGroup(GameMatcher.AllOf(GameMatcher.Position, GameMatcher.Velocity));
在上面的代码片中,我们要求一个Context为我们提供一个包含着所有拥有Position以及Velocity的Entities的Group。Group中包含的实体都是最新的,这意味着如果您从实体中删除某个Position,Entity就会从Group中被移除。如果您将Position和Velocity组件添加到Entity,它将直接进入该Group。
你可以任意使用Group,因为他们在内部是被重用的。Context会储存一个内部List保存所有你请求过的Group,所以如果你使用相同的Matcher访问Group,你就会获取到一个已经存在的引用。既然说到Matchers。。。
Matcher 匹配器
Matcher是我们如何描述我们感兴趣的Entity的一种方式。你可以说它就是我们的小型查询语言(query language)。 GameMatcher
意味着我们有一个Game
Context(请参阅Context章节中的多个Context类型部分),我们可以访问与此Context相关的所有Component类型。如果我们写context.GetGroup(GameMatcher.Position);
我们会得到一组包含Position
组件的Entites。为了定义更复杂的Group,我们可以使用AllOf
,AnyOf
和NoneOf
方法。 “AllOf”表示所有列出的Component都必须出现在Entity中才能使此Entity成为Group的一部分;“AnyOf”表示必须存在列出的组件中的一个;而在NoneOf
的情况下,我们则不希望任何列出的组件存在。 NoneOf
不是一个独立的描述,这意味着你将无法编写context.GetGroup(GameMatcher.NoneOf(GameMatcher.Position));
因为它会创建一个非常大的集合,所以是被禁止单独使用的。 NoneOf
只能与AllOf
或AnyOf
结合使用。
context.GetGroup(GameMatcher.AllOf(GameMatcher.Position, GameMatcher.Velocity).NoneOf(GameMatcher.NotMovable));
这样子我们就能得到一个拥有Position
,Velocity
但是又没有NotMovable
组件的Group Of Entities了。
AllOf
和 AnyOf
也能被组合成: context.GetGroup(Matcher.AllOf(Matcher.A, Matcher.B).AnyOf(Matcher.C, Matcher.D).NoneOf(Matcher.E))
matcher也能是从AnyOf
开始的: context.GetGroup(Matcher.AnyOf(Matcher.C, Matcher.D).NoneOf(Matcher.E))
Group observation 组合的观察
就像我说过的,Group永远都是最新的,所以他会提供一个非常方便的方法让我们观察它,并且当Entity加入、移除时接收到通知。更重要的是,但我们replace(置换)一个entity上的component时,旧的component会被移除然后加上新的component。这就意味着我们的entity会暂时离开然后马上重新加入group。这就给我们的响应式编程提供一个良好的基础。
在Entitas-CSharp中,我们不会真的删除或者添加一个Component。生成出来的代码会先向用户请求新的值,触发移除component的事件,设置一个新的值给这个component,然后触发一次增加component的事件。用这个方法,我们就避免了内存的分配以及模拟了一个在使用不可修改
(immutable)component的感觉。
Group里面有这几种事件可以监听:
- OnEntityAdded
- OnEntityRemoved
- OnEntityUpdated
其他的原料像Collector,Index以及Reactive system都是使用这些事件。所以如果你只是日常使用的话,你完全可以直接用他们。但是如果你想制作一些自定义的东西,你可能需要了解其中的实现细节。
网友评论