103_Context 上下文

上下文 Context

Context是一種為Entity服務的的管理性數據結構。一個Entity不能自己獨立創建,它必須通過context.CreateEntity()創建。通過這種方式,Context可以管理我們創建的所有Entity的生命周期。它也是第一個在我們操作Entity時得到通知的觀察者(請參閱Entity章節中的Entity觀察(Entity observation )部分)。

Entity對象池

為了避免GC,Entitas-CSharp中的Context具有內部對象池機制。當用戶創建一個新的Entity時,對象池將使用先前被銷毀而存儲起來的Entity。這種方式堆上的內存得到回收。一個Entity只有在我們確信沒有任何地方引用到的時候才會被回收。這就是Entitas-CSharp具有內部引用計數(internal reference count)機制的原因。如果您僅使用Entitas來記錄,並且不存在任何自己存儲的引用,則不必考慮它。內部類已經為您處理了所有引用的計數。但是,如果你想創建一個Component,如:

class Neighbour: IComponent { public IEntity reference;}

或者擁有一個MonoBehaviour儲存了Entity的引用:

class EntityLink : MonoBehaviour { IEntity _entity;}

如果這樣的話,你需要調用_entity.Retain(this);來儲存一個引用。當你不再需要這個Entity、或者Entity被銷毀的時候,你要記得調用_entity.Release(this);。如果你忘記調用Release,一個被銷毀的Entity將被永久保留在內存中並且不會被重用。非常容易導致內存泄漏,這在Entitas Visual Debugger中很容易觀察得到。如果你忘記調用Retain,你可能會得到一個Entity的轉世版本(reincarnated version)。這將導致非常難以調試,並且非常奇怪的行為。

順便說一句,我們不鼓勵那些Component裡面引用另一個Entity,更偏向的是使用Entity Index 的Component(參見索引(Index)章節)。而EntityLink現在是Entitas.Unity插件的一部分,所以如果你只需要引用GameObject上的Entity,不必擔心,有我們呢。

多個Context類型

如果我們將一個典型的關係資料庫(基於表結構的)與Entitas進行比較,我們可以得出以下聯繫。一個Component是一個列(column),一個Entity是一個行(row),Context(context)是一個表(table)本身。現在在關係資料庫中,一個表是根據結構(scheme)來定的。在Entitas中,它基於實現IComponent的類。這意味著當我們定義更多的Component類時,我們表的表頭就變得更廣泛。對於不同的實現細節,對內存消耗會有不同的影響。對Entitas-CSharp來說,它對內存消耗的確有一定的影響,因為一個Entity就是一個IComponent的數組。

為了解決日益增長的表格大小問題,我們可以引入另一個表格。

這裡是Entitas-Csharp Wiki的一個片段:

using Entitas;using Entitas.CodeGenerator;[Game, UI]public class SceneComponent : IComponent{ public Scene Value;}[Game]public class Bullet{ // Since it doesnt derive from IComponent // it will be generated as BulletComponent}[Meta]public struct EditorOnlyVisual{ public bool ShowInMode; public EditorOnlyVisual(bool show) { this.ShowInMode = show; }}

Component類聲明之上的屬性告訴代碼生成器我們想要擁有哪些Context類型。在這個特定的例子中,我們有一個GameMetaUIContext。正如你看到的SceneComponent一樣,一個Component可以是多個Context的一部分。這樣做的意義是,如果我們想要用關係型資料庫(realtional database)的模型來解釋的話,則表Game和表UI都應該要可以有Scene列。

我應該建立多少種Context類型呢?

這真的取決於你的應用場景。如果你有一個相當小/簡單的遊戲,你可以只使用一個Context,這種方式更簡單。您需要記住的是一個Entity是由一個Icomponent數組構成的,它是一個指針數組。指針在64bit的系統結構的大小為8個bytes。所以如果你有50個Component,每個Component的大小至少為400bytes。如果您的遊戲中有100個Entity,則它們佔用40KB。40KB是否很多的消耗全由你自己判斷,如果您有數百個Component和數千個Entity,最好開始分片管理。

有時即使是為了更好的組織,將Component分成不同的Context也是非常有好處的。您可能有些只用在遊戲核心邏輯中的Component,或者只是與元遊戲(meta game指monobehavior一類)相關的Component。如果確定Component間沒有重疊,不存在既需要存儲ComponentA和又要存儲ComponentZ的Entity。那麼最好將他們放在不同的表(tables)中。

Context的觀察(Context Observation)

與Entity相同,Context的改變也是可以觀察的。這也是我們在內部功能組(Group 本章將會介紹)和可視化調試器(visual debugger)中使用的事件。

如果您想為Entitas編寫一些工具,例如自定義記錄(Logging)或分析(Profilling)、您可以使用以下事件:

  • OnEntityCreated
  • OnEntityWillBeDestroyed
  • OnEntityDestroyed
  • OnGroupCreated

推薦閱讀:

TAG:遊戲開發 | Unity遊戲引擎 |