Context介紹
Context是Entitas中的上下文環(huán)境,主要用于管理當(dāng)前環(huán)境下的所有Entity以及Group的創(chuàng)建與回收??梢酝瑫r(shí)存在多個(gè)Context,Context之間互不影響。所有的Context由Contexts單例管理。
一、Contexts
Contexts繼承自IContexts,主要用于創(chuàng)建和保存當(dāng)前所有的Context。Contexts類由Entitias的代碼生成器生成,無(wú)須我們手動(dòng)實(shí)現(xiàn)。在Entitas系統(tǒng)外部通過Contexts.sharedInstance單例對(duì)象訪問.
public partial class Contexts : Entitas.IContexts {
public static Contexts sharedInstance {
get {
if (_sharedInstance == null) {
_sharedInstance = new Contexts();
}
return _sharedInstance;
}
set { _sharedInstance = value; }
}
static Contexts _sharedInstance;
public GameContext game { get; set; }
public InputContext input { get; set; }
public Entitas.IContext[] allContexts { get { return new Entitas.IContext [] { game, input }; } }
public Contexts() {
game = new GameContext();
input = new InputContext();
}
}
二、ContextInfo
ContextInfo是Context的信息類,是Context初始化參數(shù)之一.主要包含了Context的名字,Context所有組件名字?jǐn)?shù)組以及所有組件類型數(shù)組,而這些信息最終也是用來初始化Entity的.
public ContextInfo(string name, string[] componentNames, Type[] componentTypes) {
this.name = name;
this.componentNames = componentNames;
this.componentTypes = componentTypes;
}
三、Context
Context繼承自IContext,用來管理當(dāng)前上下文中的Entity以及Group的.我們所有用到的Enity與Group都應(yīng)該通過Context的CreateEntity()與GetGroup(IMatcher<TEntity> matcher)方法創(chuàng)建與獲取.因?yàn)樵贑ontext中會(huì)對(duì)所有的Entity與Group進(jìn)行緩存,回收利用.
3.1、Context創(chuàng)建
Context無(wú)需我們手動(dòng)創(chuàng)建,在Entitas的generate時(shí),代碼生成器會(huì)為我們自動(dòng)生成對(duì)應(yīng)的Context.下面的代碼為自動(dòng)生成的代碼.
public sealed partial class GameContext : Entitas.Context<GameEntity> {
public GameContext()
: base(
GameComponentsLookup.TotalComponents,
0,
new Entitas.ContextInfo(
"Game",
GameComponentsLookup.componentNames,
GameComponentsLookup.componentTypes
),
(entity) =>
#if (ENTITAS_FAST_AND_UNSAFE)
new Entitas.UnsafeAERC(),
#else
new Entitas.SafeAERC(entity),
#endif
() => new GameEntity()) {
}
}
我們?cè)賮砜纯碈ontext構(gòu)造函數(shù)的實(shí)現(xiàn):
public Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo, Func<IEntity, IAERC> aercFactory, Func<TEntity> entityFactory) {
//當(dāng)前環(huán)境中組件的類型的數(shù)量,主要用來初始化組件列表的初始化長(zhǎng)度
_totalComponents = totalComponents;
//當(dāng)前環(huán)境中Entity的起始ID
_creationIndex = startCreationIndex;
if (contextInfo != null) {
_contextInfo = contextInfo;
if (contextInfo.componentNames.Length != totalComponents) {
throw new ContextInfoException(this, contextInfo);
}
} else {
_contextInfo = createDefaultContextInfo();
}
//創(chuàng)建Entity的引用計(jì)數(shù)的工廠方法
_aercFactory= aercFactory ?? (entity => new SafeAERC(entity));
//創(chuàng)建Entity的工廠方法
_entityFactory = entityFactory;
//初始化各種容器
_groupsForIndex = new List<IGroup<TEntity>>[totalComponents];
_componentPools = new Stack<IComponent>[totalComponents];
_entityIndices = new Dictionary<string, IEntityIndex>();
_groupChangedListPool = new ObjectPool<List<GroupChanged<TEntity>>>(
() => new List<GroupChanged<TEntity>>(),
list => list.Clear()
);
//緩存delegates避免gc分配
//主要時(shí)在組件或者實(shí)體發(fā)生改變時(shí),在代理方法中去改變對(duì)應(yīng)Group中所包含的內(nèi)容
//這也是Entitas處理大量同類型數(shù)據(jù)比較快時(shí)原因之一,避免的大量的循環(huán)遍歷
_cachedEntityChanged = updateGroupsComponentAddedOrRemoved;
_cachedComponentReplaced = updateGroupsComponentReplaced;
_cachedEntityReleased = onEntityReleased;
_cachedDestroyEntity = onDestroyEntity;
3.2、Entity創(chuàng)建
每個(gè)Context中都有一個(gè)保存當(dāng)前環(huán)境Entity的對(duì)象池:_reusableEntities,以及當(dāng)前所有活躍的Entity的集合_entities。記住,所有的Entity創(chuàng)建都需要通過Context的CreateEntity()方法。這樣通過對(duì)象池可以減少大量的創(chuàng)建Entity的時(shí)間開銷,同時(shí)也避免頻繁的創(chuàng)建銷毀Entity所帶來的內(nèi)存碎片,提高內(nèi)存使用率。
public TEntity CreateEntity() {
TEntity entity;
//如果對(duì)象池中有對(duì)象,則取出對(duì)象,并重新激活
//如果沒有,則使用工廠方法創(chuàng)建一個(gè)新的Entity,并初始化
if (_reusableEntities.Count > 0) {
entity = _reusableEntities.Pop();
entity.Reactivate(_creationIndex++);
} else {
entity = _entityFactory();
entity.Initialize(_creationIndex++, _totalComponents, _componentPools, _contextInfo, _aercFactory(entity));
}
//加入活躍列表中,并增加引用計(jì)數(shù)
_entities.Add(entity);
entity.Retain(this);
_entitiesCache = null;
//給Entity的變化添加代理方法,用于更新Group
entity.OnComponentAdded += _cachedEntityChanged;
entity.OnComponentRemoved += _cachedEntityChanged;
entity.OnComponentReplaced += _cachedComponentReplaced;
entity.OnEntityReleased += _cachedEntityReleased;
entity.OnDestroyEntity += _cachedDestroyEntity;
if (OnEntityCreated != null) {
OnEntityCreated(this, entity);
}
return entity;
}
3.3、Group的創(chuàng)建
Context中保存這當(dāng)前環(huán)境中的所有Group,同時(shí)通過代理,在Entity發(fā)生變化時(shí),更新對(duì)應(yīng)的Group。保存所有Matcher與對(duì)應(yīng)Group的字典Dictionary<IMatcher<TEntity>, IGroup<TEntity>> _groups,主要用于獲取Group,主要用于Component變化時(shí)更新Group。
public IGroup<TEntity> GetGroup(IMatcher<TEntity> matcher) {
IGroup<TEntity> group;
//查看_groups中是否已存在該matcher的Group,有則返回,沒有就創(chuàng)建新的Group
if (!_groups.TryGetValue(matcher, out group)) {
group = new Group<TEntity>(matcher);
var entities = GetEntities();
//遍歷所有的Entity,將匹配的加入到Group中
for (int i = 0; i < entities.Length; i++) {
group.HandleEntitySilently(entities[i]);
}
_groups.Add(matcher, group);
//遍歷這個(gè)matcher中的所有Component序號(hào),將Group加入到對(duì)應(yīng)的_groupsForIndex中
for (int i = 0; i < matcher.indices.Length; i++) {
var index = matcher.indices[i];
if (_groupsForIndex[index] == null) {
_groupsForIndex[index] = new List<IGroup<TEntity>>();
}
_groupsForIndex[index].Add(group);
}
if (OnGroupCreated != null) {
OnGroupCreated(this, group);
}
}
return group;
}
3.4、其他
還有一些其他的方法,例如Entity上組件添加或修改時(shí)的代理方法updateGroupsComponentAddedOrRemoved,組件刪除時(shí)的代理方法onDestroyEntity等這些大家可以通過查看Context的源碼進(jìn)行了解。
四、總結(jié)
Context是一個(gè)獨(dú)立的環(huán)境,使用對(duì)象池管理當(dāng)前環(huán)境中所有的Entity的創(chuàng)建以及回收。緩存著所有的Group,同時(shí)通過設(shè)置Entity改變時(shí)的一系列的代理方法,更新當(dāng)前環(huán)境中對(duì)應(yīng)的Group。
這樣做可以減少Entity創(chuàng)建的開銷,減少因?yàn)镋ntity頻繁的創(chuàng)建與消耗帶來的內(nèi)存碎片,提高內(nèi)存使用率。同時(shí)減少了我們需要某些數(shù)據(jù)集合時(shí),通過遍歷帶來的一次集中性的大的開銷,將這些分散到各個(gè)Entity變化的時(shí)刻。
<上一篇> ECS Entitas分析(一)___概括