ECS中所有实体以ID的方式进行存储,正常游戏物体要分很多种类型,还要对不同类型分别进行操作,这就需要筛选器来获取你想要的操作的实体对象进行修改数据。
案例十四:EntityQuery(实体筛选器)(Scne15)
用到的ComponetData:
using Unity.Entities;
public struct ComponentA : IComponentData
{
public int type;
}
public struct ComponentB : IComponentData
{
}
public struct ComponentC : IComponentData
{
}
public struct ComponentD : IComponentData
{
}
public struct ShareComponentA : ISharedComponentData
{
public int type;
}
下面介绍各种各种筛选方法
第一种:我们先前经常用到的,从场景里获取包含对应组件的实体 : GetEntityQuery() 是抽象类:ComponentSystemBase中的方法
using Unity.Entities;
using Unity.Jobs;
public class Query_GetEntityQuerySystem : JobComponentSystem
{
private EntityQuery entityQuery;
protected override void OnCreate()
{
// 通过得到只包含ComponentA和ComponentB组件实体的筛选器
entityQuery = this.GetEntityQuery(typeof(ComponentA), typeof(ComponentB));
}
// IJobForEach也可以充当筛选器的作用,但是不如如上灵活方便
struct EntityQueryJob : IJobForEach<ComponentA>
{
public void Execute(ref ComponentA a)
{
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
EntityQueryJob entityQueryJob = new EntityQueryJob()
{
};
//讲这个值传入Job使用
JobHandle jobHandle = entityQueryJob.Schedule(entityQuery, inputDeps);
jobHandle.Complete();
return jobHandle;
}
}
第二种:通过创建EntityQueryDesc对象,它位于Unity.Entites命名空间,该对象内部提供了 Any、None、All三种类型数组。
using Unity.Entities;
using Unity.Jobs;
//对于筛选更为复杂得到实体,或 且 不包含 可以使用EntityQueryDes 筛选器
public class Query_EntityQueryDesSystem : JobComponentSystem
{
private EntityQuery entityQuery;
protected override void OnCreate()
{
//-- 第一种
var query = new EntityQueryDesc()
{
All = new ComponentType[] { typeof(ComponentA), typeof(ComponentB), typeof(ComponentC) }
};
//代表要筛选同时包含这个数组里所有类型的组件的实体。
this.entityQuery = GetEntityQuery(query);
//-- 第二种
var twoQuery = new EntityQueryDesc()
{
All = new ComponentType[] { typeof(ComponentA) },
Any = new ComponentType[] { typeof(ComponentB), typeof(ComponentC) },
None = new ComponentType[] { typeof(ComponentD) }
};
// 筛选必须包含A组件 ,包含 ComponentB或者C组件,不包含D组件的实体!
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
return inputDeps;
}
}
第三种:SetFilter (我从其他文章介绍看到的是SetSharedComponentFilter但是这个版本没有该方法我觉得是改成这个名字了) 筛选共享组件,这里操作的数据组件必须得是共享的数据,也就是Data:IShareComponent这种类型的。通过指定条件来实现筛选操作。它属于 EntityQuery中的一个方法。
using Unity.Entities;
using Unity.Jobs;
public class Query_SetSharedComponentFilter : JobComponentSystem
{
EntityQuery entityQuery;
protected override void OnCreate()
{
entityQuery = GetEntityQuery(typeof(ShareComponentA));
entityQuery.SetFilter<ShareComponentA>(new ShareComponentA { type = 1 });
// 注意: 这里的实体上数据结构 是继承自 ISahredComponentData
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
return inputDeps;
}
}
如上,指定筛选的组件type必须为1,再将其传入Job中使用。
第四种:SetFilterChanged(从其他文章看到的是SetSharedComponentFilter),通过判定该实体的指定的组件发生了改变来进行筛选。
using Unity.Entities;
using Unity.Jobs;
public class Query_SetChangedVersionFilter : JobComponentSystem
{
EntityQuery entityQuery;
protected override void OnCreate()
{
entityQuery = GetEntityQuery(typeof(ComponentA), typeof(ComponentB));
entityQuery.SetFilterChanged(typeof(ComponentA));
// 筛选 指定该实体的哪个组件发生了改变
}
struct ChangeJob : IJobForEach<ComponentA>
{
public void Execute(ref ComponentA comA)
{
// 注意我在Job中并没有修改,但是这里也判定了 ComponentA被修改了
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
return inputDeps;
}
}
SetFilterChanged 会对指定组件被修改了的进行筛选,注意:这里的被修改,实则是被传入标记了,比如上面Job中 ref 标记读写操作。这也算被修改了。而且这个修改也影响了整个Chunk(块),我的理解是如果对该Chunk使用该筛选方式时,一个实体被标记了整个Chunk也是被标记的。因此平时开发时要对不需要操作的组件进行只读(Readonly)标记。
远程项目仓库(github):https://github.com/h1031464180/UnityECS