Entitas源码分析-Context
Context(上下文)
Context在Entitas中可以理解为一个世界、场景,它存放了当前所有Entity,并负责对Entity进行管理。
一般情况下是通过在Entitas
的Jenny
菜单的Contexts
配置项自动生成。先来看看自动生成的Context是什么样的:
自动生成的代码
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()
) {}
}
我们可以看到自动生成的Context实际上是调用了Entittas中的Context的构造函数。
totalComponents: 当前这个context全部组件数量。 运行时通过这个数字来开辟相对应的内存。 为了提高效率可以使用 ContextAttribute 特性来让组件只在指定的context中可。
startCreationIndex: 开始创建 Context 中的 Entity 时的下标, 一般为0不用动
ContextInfo:主要用于表述当前Context的信息。
aercFactory: 产生引用计数对象的工厂函数
entityFactory: 产生Entity的工厂函数。 先是从内部的对象池中获取,如果没有可用的Entity则会调用这个函数来产生一个新的Entity
注意:这个类添加了partial关键字。 当对Component使用 Unique 特性后会生成对应的代码。 就可以在对应的Context上直接添加对应的组件,而不仅限于给Entity添加组件。
管理Entity
我们可以从生成的GameContext看到,生成的Entity也是自动生成的GameEntity。并且通过工厂函数的方式供内部使用。那么Context对Entity提供了哪些操作呢?
// IContext.cs
public interface IContext<TEntity> : IContext where TEntity : class, IEntity {
TEntity CreateEntity(); // 创建
bool HasEntity(TEntity entity); // 查询
TEntity[] GetEntities(); // 获取所有Entity
IGroup<TEntity> GetGroup(IMatcher<TEntity> matcher); // 查找对应分组下的所有Entity
}
查看源码可以知道实际上只提供了简单的创建和查询的功能。
注意:Context中管理Entity内容较少, 但是会向创建出来的Entity注册对应的事件。 处理entity创建、销毁等事件。同时Context也根据Entity的事件派发对应的事件。
EntityIndex
查询
另外有时有通过Entity中某些字段来快速查找到对应的Entity。 这个时候我们就需要使用EntityIndex功能来实现了。通过在组件上的字段添加 AbstractEntityIndexAttribute 特性让代码生成工具生成对应的代码:
// 在组件上设置
public class IdComponent : IComponent
{
[PrimaryEntityIndex] // <<-- 看这里
public ulong value;
}
// 代码生成 注册
public partial class Contexts {
public const string Id = "Id";
[Entitas.CodeGeneration.Attributes.PostConstructor]
public void InitializeEntityIndices() {
game.AddEntityIndex(new Entitas.PrimaryEntityIndex<GameEntity, ulong>(
Id,
game.GetGroup(GameMatcher.Id),
(e, c) => ((IdComponent)c).value)
);
}
}
// 代码生成 查询
public static class ContextsExtensions {
public static GameEntity GetEntityWithId(this GameContext context, ulong value) {
return ((Entitas.PrimaryEntityIndex<GameEntity, ulong>)context.GetEntityIndex(Contexts.Id)).GetEntity(value);
}
}
EntityIndex的原理是通过设置给它的group获取对应的Entity,同时注册gourp中entity添加删除的事件来保持数据同步。当需要查询时直接通过存储好的字典进行处理。
管理Components
实际上Component是添加在Entity上的,需要管理组件相关的内容只有一个组件对象池。 在创建Entity的时候将组件对象池及组件相关信息传入。所有的Entity公用对象池。对象池的声明如下:
readonly Stack<IComponent>[] _componentPools;
通过组件的下标可以快速的在数组中索引到对应的堆栈。其他的操作就是简单的拿取放回。