Collector创建
Collector可以用来监听group中的entity。我们可以通过如下方式创建:
var group = gameContext.GetGroup(GameMatcher.Position);
var colloect = group.CreateCollector(GroupEvent.Removed);
这段代码表示当group中的entity移除Position组件时将会被添加到collect中。 不同行为通过GroupEvent定义,默认为GroupEvent.Add。
- GroupEvent.Add:表示entity添加Position组件,进入group的同时,也会添加到collect中。
- GroupEvent.Removed:表示entity移除Position组件时,会进入collect中。
- GroupEvent.AddOrRemoved:表示移除或添加Position组件都会进入collect。
Collector原理
以下面这段代码为例,解释Collecter的原理。
var group = gameContext.GetGroup(GameMatcher.Position);
var colloect = group.CreateCollector(GroupEvent.Removed);
首先跟踪一下CreateCollector()方法,如下:
public static ICollector<TEntity> CreateCollector<TEntity>(this IGroup<TEntity> group, GroupEvent groupEvent = GroupEvent.Added) where TEntity : class, IEntity
{
return (ICollector<TEntity>) new Collector<TEntity>(group, groupEvent);
}
该方法为拓展方法,只是new了一个Collect的实例。在查看一下Collector的构造函数:
//Collector.cs
public Collector(IGroup<TEntity>[] groups, GroupEvent[] groupEvents)
{
this._groups = groups;
this._collectedEntities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
this._groupEvents = groupEvents;
if (groups.Length != groupEvents.Length)
throw new CollectorException("Unbalanced count with groups (" + (object) groups.Length + ") and group events (" + (object) groupEvents.Length + ").", "Group and group events count must be equal.");
this._addEntityCache = new GroupChanged<TEntity>(this.addEntity);
this.Activate();
}
这段代码最关键的为这两句代码,_addEntityCache为一个事件,对应的方法签名为addEntity。所以接下来分别查看一下addEntity方法,和Activate方法。
this._addEntityCache = new GroupChanged<TEntity>(this.addEntity);
this.Activate();
首先是addEntity方法
//Collector.cs
private void addEntity(IGroup<TEntity> group, TEntity entity, int index, IComponent component)
{
if (!this._collectedEntities.Add(entity))
return;
entity.Retain((object) this);
}
_collectedEntities为HashSet,保证了不会包含重复的entity,所以,执行_addEntityCache事件时,就会将entity添加到名为_collectedEntities的HashSet集合中。接下来为Activate方法:
//Collector.cs
public void Activate()
{
for (int index = 0; index < this._groups.Length; ++index)
{
IGroup<TEntity> group = this._groups[index];
switch (this._groupEvents[index])
{
case GroupEvent.Added:
group.OnEntityAdded -= this._addEntityCache;
group.OnEntityAdded += this._addEntityCache;
break;
case GroupEvent.Removed:
group.OnEntityRemoved -= this._addEntityCache;
group.OnEntityRemoved += this._addEntityCache;
break;
case GroupEvent.AddedOrRemoved:
group.OnEntityAdded -= this._addEntityCache;
group.OnEntityAdded += this._addEntityCache;
group.OnEntityRemoved -= this._addEntityCache;
group.OnEntityRemoved += this._addEntityCache;
break;
}
}
}
从上可以看出,OnEntityRemoved也是一个事件,并且触发时,与_addEntityCache的效果一样,接下来从Group的实现代码中看一下OnEntityRemoved的触发情况。
//Group.cs
public void UpdateEntity(TEntity entity, int index, IComponent previousComponent,
IComponent newComponent)
{
if (!this._entities.Contains(entity))
return;
// ISSUE: reference to a compiler-generated field
if (this.OnEntityRemoved != null)
{
// ISSUE: reference to a compiler-generated field
this.OnEntityRemoved((IGroup<TEntity>) this, entity, index, previousComponent);
}
// ISSUE: reference to a compiler-generated field
if (this.OnEntityAdded != null)
{
// ISSUE: reference to a compiler-generated field
this.OnEntityAdded((IGroup<TEntity>) this, entity, index, newComponent);
}
// ISSUE: reference to a compiler-generated field
if (this.OnEntityUpdated == null)
return;
// ISSUE: reference to a compiler-generated field
this.OnEntityUpdated((IGroup<TEntity>) this, entity, index, previousComponent, newComponent);
}
再来查看那个位置调用了UpdateEntity,代码如下:
//Context.cs
private void updateGroupsComponentReplaced(IEntity entity, int index, IComponent previousComponent, IComponent newComponent)
{
List<IGroup<TEntity>> groupList = this._groupsForIndex[index];
if (groupList == null)
return;
TEntity entity1 = (TEntity) entity;
for (int index1 = 0; index1 < groupList.Count; ++index1)
groupList[index1].UpdateEntity(entity1, index, previousComponent, newComponent);
}
接下来在查找updateGroupsComponentReplaced方法使用位置,代码如下:
//Context.cs
public Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo, Func<IEntity, IAERC> aercFactory, Func<TEntity> entityFactory)
{
//····省略前面代码
this._cachedComponentReplaced = new EntityComponentReplaced(this.updateGroupsComponentReplaced);
this._cachedEntityReleased = new EntityEvent(this.onEntityReleased);
this._cachedDestroyEntity = new EntityEvent(this.onDestroyEntity);
}
接下来可以追踪_cachedComponentReplaced事件使用位置,代码如下:
//Context.cs
public TEntity CreateEntity()
{
// ·····省略前面的代码
entity.OnComponentReplaced += this._cachedComponentReplaced;
entity.OnEntityReleased += this._cachedEntityReleased;
entity.OnDestroyEntity += this._cachedDestroyEntity;
// ISSUE: reference to a compiler-generated field
if (this.OnEntityCreated != null)
{
// ISSUE: reference to a compiler-generated field
this.OnEntityCreated((IContext) this, (IEntity) entity);
}
return entity;
}
可以发现在创建entity时,赋值给了entity的OnComponentReplaced事件。接下俩查看OnComponentReplaced事件的引用位置。代码如下:
//Enity.cs
private void replaceComponent(int index, IComponent replacement)
{
IComponent component = this._components[index];
if (replacement != component)
{
this._components[index] = replacement;
this._componentsCache = (IComponent[]) null;
if (replacement != null)
{
// ISSUE: reference to a compiler-generated field
if (this.OnComponentReplaced != null)
{
// ISSUE: reference to a compiler-generated field
this.OnComponentReplaced((IEntity) this, index, component, replacement);
}
}
else
{
this._componentIndicesCache = (int[]) null;
this._toStringCache = (string) null;
// ISSUE: reference to a compiler-generated field
if (this.OnComponentRemoved != null)
{
// ISSUE: reference to a compiler-generated field
this.OnComponentRemoved((IEntity) this, index, component);
}
}
this.GetComponentPool(index).Push(component);
}
else
{
// ISSUE: reference to a compiler-generated field
if (this.OnComponentReplaced == null)
return;
// ISSUE: reference to a compiler-generated field
this.OnComponentReplaced((IEntity) this, index, component, replacement);
}
}
这段代码的意思是,先查看该entity是否存在有该component,若存在,则触发OnComponentReplaced事件,否则将触发OnComponentRemoved事件。 接下来查找一下replaceComponent的使用位置。
//Enity.cs
ublic void ReplaceComponent(int index, IComponent component)
{
if (!this._isEnabled)
throw new EntityIsNotEnabledException("Cannot replace component '" + this._contextInfo.componentNames[index] + "' on " + (object) this + "!");
if (this.HasComponent(index))
{
this.replaceComponent(index, component);
}
else
{
if (component == null)
return;
this.AddComponent(index, component);
}
}
ReplaceComponent方法是不是很熟悉,随便打开一个带参数的component的生成代码,都可以发现此方法。例如:
public partial class GameEntity {
//····省略前面代码
public void ReplaceAssetName(string newAsset_name) {
var index = GameComponentsLookup.AssetName;
var component = (AssetNameComponent)CreateComponent(index, typeof(AssetNameComponent));
component.asset_name = newAsset_name;
ReplaceComponent(index, component);
}
//····省略后面代码
}
所以当调用entity.ReplaceAssetName("22")
时,就会触发系列的反应,该entity也会被收集起来。
接着调用colloect.collectedEntities便可以获得收集到的entities.
总结
从以上流程也可以发现,Collector只可以添加,也就意味着,假如我们收集的是添加Position Component,但是Position Component移除后,entity并不会从Collector中除去,所以要小心使用,但是ReactiveSystem很好的解决了这个问题,请看下一篇解析。