我们上章谈过ComponentSystem需要为注入的字段找到符合条件的Archetype链表(m_FirstMatchingArchetype),有两种实现方式,一是每次Update前都遍历所有Archetype找一次,这样简单粗暴但过于低效;复杂点的是做法二,在创建好System时就找一次,之后有新的Archetype创建时就判断一下是否满足条件,是则加进来。
我们先从最简单的例子看起:
public class SampleSystem : ComponentSystem
{
public struct Data
{
public readonly int Length;
public ComponentDataArray<Position> Position;
public ComponentDataArray<Rotation> Rotation;
}
[Inject] private Data m_Data;
protected override void OnUpdate()
{
for (int i = 0; i != m_Data.Length; i++)
{
var pos = m_Data.Position[i].Value;
}
}
}
实际上,在每个System初始化时就会针对每个Inject的字段生成一个ComponentGroup对象,其保存着诸如上章所讲的FirstMatchingArchetype链表等信息,所以上面的代码等价于:
public class SampleSystem : ComponentSystem
{
ComponentGroup m_Group;
protected override void OnCreateManager()
{
m_Group = GetComponentGroup(typeof(Position), typeof(Rotation));
}
protected override void OnUpdate()
{
var positions = m_Group.GetComponentDataArray<Position>();
for (int i = 0; i != positions.Length; i++)
{
var pos = positions[i].Value;
}
}
}
下面版本的写法也照样可以正常运行的,且更加灵活,可以每帧通过SetFilter指定过滤项,如:
protected override void OnUpdate()
{
//只处理SharedCom1共享组件的值为1的组件
m_Group.SetFilter(new SharedCom1 { Value = 1 });
var positions = m_Group.GetComponentDataArray<Position>();
for (int i = 0; i != positions.Length; i++)
{
var pos = positions[i].Value;
}
}
回到正题,ComponentGroup大部分数据保存在m_GroupData字段里,有FirstMatchingArchetype链表和所需的组件集合,还有需排除的组件集合(用SubtractiveComponent包裹如SubtractiveComponent<Scale> Scale;就是指需要没有Scale组件才满足条件)。
同一个World里的所有Archetype都由ArchetypeManager管理着,所以可以遍历所有Archetype看看有哪些满足条件的就加入FirstMatchingArchetype链表,详细代码可以在创建ComponentGroup时看到: