104_Group 組合
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都是使用這些事件。所以如果你只是日常使用的話,你完全可以直接用他們。但是如果你想製作一些自定義的東西,你可能需要了解其中的實現細節。
推薦閱讀: