ECS Entitas分析(二)__Context

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分析(一)___概括

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容