上下文 Context
Context是一種為Entity服務的的管理性數(shù)據(jù)結構囤热。一個Entity不能自己獨立創(chuàng)建,它必須通過context.CreateEntity()
創(chuàng)建岖妄。通過這種方式,Context可以管理我們創(chuàng)建的所有Entity的生命周期币叹。它也是第一個在我們操作Entity時得到通知的觀察者(請參閱Entity章節(jié)中的Entity觀察(Entity observation )
部分)绣檬。
Entity對象池
為了避免GC璧眠,Entitas-CSharp中的Context具有內部對象池機制缩焦。當用戶創(chuàng)建一個新的Entity時,對象池將使用先前被銷毀而存儲起來的Entity责静。這種方式堆上的內存得到回收袁滥。一個Entity只有在我們確信沒有任何地方引用到的時候才會被回收。這就是Entitas-CSharp具有內部引用計數(shù)(internal reference count)機制的原因灾螃。如果您僅使用Entitas來記錄题翻,并且不存在任何自己存儲的引用,則不必考慮它腰鬼。內部類已經(jīng)為您處理了所有引用的計數(shù)嵌赠。但是,如果你想創(chuàng)建一個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)章節(jié))纫溃。而EntityLink
現(xiàn)在是Entitas.Unity
插件的一部分腰涧,所以如果你只需要引用GameObject上的Entity,不必擔心紊浩,有我們呢窖铡。
多個Context類型
如果我們將一個典型的關系數(shù)據(jù)庫(基于表結構的)與Entitas進行比較,我們可以得出以下聯(lián)系坊谁。一個Component是一個列(column)费彼,一個Entity是一個行(row),Context(context)是一個表(table)本身】谏郑現(xiàn)在在關系數(shù)據(jù)庫中箍铲,一個表是根據(jù)結構(scheme)來定的。在Entitas中鬓椭,它基于實現(xiàn)IComponent
的類颠猴。這意味著當我們定義更多的Component類時,我們表的表頭就變得更廣泛小染。對于不同的實現(xiàn)細節(jié)翘瓮,對內存消耗會有不同的影響。對Entitas-CSharp來說裤翩,它對內存消耗的確有一定的影響资盅,因為一個Entity就是一個IComponent
的數(shù)組。
為了解決日益增長的表格大小問題踊赠,我們可以引入另一個表格呵扛。
這里是Entitas-Csharp Wiki的一個片段:
using Entitas;
using Entitas.CodeGenerator;
[Game, UI]
public class SceneComponent : IComponent
{
public Scene Value;
}
[Game]
public class Bullet
{
// Since it doesn't 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類型。在這個特定的例子中筐带,我們有一個Game
今穿,Meta
和UI
Context。正如你看到的SceneComponent
一樣烫堤,一個Component可以是多個Context的一部分荣赶。這樣做的意義是凤价,如果我們想要用關系型數(shù)據(jù)庫(realtional database)的模型來解釋的話,則表Game
和表UI
都應該要可以有Scene
列拔创。
我應該建立多少種Context類型呢利诺?
這真的取決于你的應用場景。如果你有一個相當小/簡單的游戲剩燥,你可以只使用一個Context慢逾,這種方式更簡單。您需要記住的是一個Entity是由一個Icomponent
數(shù)組構成的灭红,它是一個指針數(shù)組侣滩。指針在64bit的系統(tǒng)結構的大小為8個bytes。所以如果你有50個Component变擒,每個Component的大小至少為400bytes君珠。如果您的游戲中有100個Entity,則它們占用40KB娇斑。40KB是否很多的消耗全由你自己判斷策添,如果您有數(shù)百個Component和數(shù)千個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