Unity ECS+Jobs System笔记 访问数据4(八)

来源:https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/index.html
我会对官方文档内容略作整理,有需要可以查看官方文档

6、Component Groups

读取或写入数据的第一步是查找数据,ECS框架中数据储存在组件中,组件根据它们所属的实体的原型储存在内存中,要定义ECS数据的视图,且该视图仅包含给定算法或过程所需的特定数据,就可以构建EntityQuery
在创建EntityQuery之后可以:

  • 通过Job处理视图中选择的实体和组件
  • 获取包含所有选定实体的NativeArray
  • 获取包含所有选定组件的NativeArray

EntityQuery返回的实体和组件数组是“并行”的,也就是说,相同的索引值在任何数组中都会索引到同一实体
注意: 无论是ComponentSystem.Entites.ForEach还是IJobForEach都会基于组件类型和属性根据API创建内部的EntityQueries

6.1、Defining a query(定义查询)

一个EntityQuery查询必须定义原型所包含的组件类型集合,以便将其区块和实体包含在视图中,同时也可以排除包含特定类型组件的原型
对于简单的查询,你可以基于组件类型的数组创建EntityQuery,举个例子:该EntityQuery查找包含RotationQuaternion和RotationSpeed组件的所有实体:

EntityQuery m_Group = GetEntityQuery(typeof(RotationQuaternion),
ComponentType.ReadOnly<RotationSpeed>());

查询时使用ComponentType.ReadOnly< T > 而不是更简单的typeof可以用来指定RotationSpeed在可能的情况下始终只读,这样对数据的读访问限制较少,可以帮助Job调度系统更有效地执行Job‘

6.1.1、EntityQueryDesc

对于更复杂的情况,可以使用EntityQueryDesc来创建EntityQuery,它提供了灵活的查询机制来查询指定组件类型:

  • All——原型中必须包含参数数组中所有的组件
  • Any——原型中至少要包含一个参数数组中的组件
  • None——原型中不能有任何一个参数数组中的组件

例如,以下查询包含包含RotationQuaternion和RotationSpeed组件的原型,但不包括任何包含Frozen组件的原型:

var query = new EntityQueryDesc
{
   None = new ComponentType[]{ typeof(Frozen) },
   All = new ComponentType[]{ typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>() }
}
EntityQuery m_Group = GetEntityQuery(query);

注意: 不要在EntityQueryDesc中包含完全可选的组件,要处理可选组件,请在IJobChunk.Execute()中使用chunk.Has< T >()来确定当前ArchetypeChunk是否具有可选组件,由于同一区块中的所有实体具有相同的组件,因此只需要检查每个块是否存在可选组件——而不是检查每个实体

6.1.2、查询选项

创建EntityQueryDesc时,可以设置其Options变量(通常不需要设置它们)

条件描述
Default默认
IncludePrefab该查询不会隐式的排除具有Prefab组件的实体
IncludeDisabled该查询不会隐式的排除具有Disabled(已禁用)组件的实体
FilterWriteGroup查询会根据查询中指定的组件的WriteGroupAttribute属性筛选所选实体

例如,假设C2和C3是基于C1组件写入同一组中的,并且使用FilterWriteGroup选项(需要C1和C3)创建了一个查询:

public struct C1: IComponentData{}

[WriteGroup(C1)]
public struct C2: IComponentData{}

[WriteGroup(C1)]
public struct C3: IComponentData{}

// ... In a system:
var query = new EntityQueryDesc{
    All = new ComponentType{typeof(C1), ComponentType.ReadOnly<C3>()},
    Options = EntityQueryDescOptions.FilterWriteGroup
};
var m_group = GetEntityQuery(query);

此查询排除具有C2和C3的任何实体,因为C2未明确是否包含在查询中,虽然您可以使用None这种方法将其设计到查询中,但通过写入组(Write Group)来执行它有一个重要的好处:你不需要更改其他系统使用的查询方式(只要这些系统也使用了写入组)

写入组是一种允许扩展现有系统的机制,例如,如果C1和C2在另一个系统中定义(可能是无法控制的库的一部分),则可以将C3放入与C2相同的写入组中,以便随着C1进行更新,对于添加C3组件的任何实体,系统将更新C1,而原始系统不会,对于没有C3的其他实体,原始系统将像以前一样更新C1

6.1.3、组合查询

你还可以通过传递EntityQueryDesc数组而不是单个来组合查询,使用or运算组合每个查询,以下代码查询了包含RotationQuaternion组件或RotationSpeed组件(或两者)的原型:

var query0 = new EntityQueryDesc
{
   All = new ComponentType[] {typeof(RotationQuaternion)}
};

var query1 = new EntityQueryDesc
{
   All = new ComponentType[] {typeof(RotationSpeed)}
};

EntityQuery m_Group = GetEntityQuery(new EntityQueryDesc[] {query0, query1});
6.2、创建EntityQuery

在非系统类,可以使用EntityManager.CreateEntityQuery()函数创建EntityQuery :

EntityQuery m_Group = CreateEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());

但是,在系统类中,必须使用以下GetEntityQuery()函数:

public class RotationSpeedSystem : JobComponentSystem
{
   private EntityQuery m_Group;
   protected override void OnCreate()
   {
       m_Group = GetEntityQuery(typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>());
   }
   //…
}

当需要重用相同的数据时,应尽可能缓存EntityQuery实例,而不是为每次使用创建一个新实例,例如,在一个系统中,可以在系统的OnCreate()函数中创建EntityQuery ,并将结果存储在实例的变量中,上例中的m_Group变量就是用于此目的

6.3、定义过滤器(Define Filter)

除了定义所需的查询需要包含或是不包含哪些组件,也可以对查询添加过滤器,你可以指定以类型的过滤器:

  • Shared component values——共享组件过滤器,根据共享组件的特定值过滤实体
  • Change filter——更改过滤器,根据特定组件类型的值是否潜在的被更改了来过滤实体
6.3.1、共享组件过滤器

要使用共享组件过滤器,首先要在EntityQuery中包含共享组件(以及其他所需组件),然后调用SetFilter()函数,传入要选择的值所包含的ISharedComponent类型的结构。所有值都必须匹配,且最多可以向筛选器添加两个不同的共享组件
你可以随时更改过滤器,但更改过滤器不会根据ToComponentDataArray()或ToEntityArray()函数更改现有实体或组件数组,你必须重新创建这些数组
下面这个例子,有一个定义为SharedGrouping的共享组件,和一个仅处理Group字段设置为1的实体的系统

struct SharedGrouping : ISharedComponentData
{
    public int Group;
}

class ImpulseSystem : ComponentSystem
{
    EntityQuery m_Group;

    protected override void OnCreate(int capacity)
    {
        m_Group = GetEntityQuery(typeof(Position), typeof(Displacement), typeof(SharedGrouping));
    }

    protected override void OnUpdate()
    {
        // Only iterate over entities that have the SharedGrouping data set to 1
        m_Group.SetFilter(new SharedGrouping { Group = 1 });

        var positions = m_Group.ToComponentDataArray<Position>(Allocator.Temp);
        var displacememnts = m_Group.ToComponentDataArray<Displacement>(Allocator.Temp);

        for (int i = 0; i != positions.Length; i++)
            positions[i].Value = positions[i].Value + displacememnts[i].Value;
    }
}
6.3.2、更改过滤器

如果只需在组件值更改时更新实体,则可以使用SetFilterChanged()函数将该组件添加到EntityQuery过滤器中,例如,以下EntityQuery仅包含块中在另一个系统已对Translation组件写入数据的实体:

protected override void OnCreate(int capacity)
{
    m_Group = GetEntityQuery(typeof(LocalToWorld), ComponentType.ReadOnly<Translation>());
    m_Group.SetFilterChanged(typeof(Translation));
}

请注意,为了效率,ChangeFilter会作用在整个实体的区块上,它不会追踪单个实体;同时ChangeFilter也只会检查该组件是否根据已声明的方式运行,而不是是否实际被更改了数据。如果某个区块已被另一个能够对该类型组件进行写入操作的Job所访问,那么ECS框架就会认为该区块已被更改并包含Job中的所有实体;否则ECS框架完全排除该块中的实体(这是对您不需要修改的组件声明只读访问权限的另一个原因)

6.4、执行查询

当您在Job中使用EntityQuery或者调用了一个在视图中返回entities、components或chunks的EntityQuery方法就会使用EntityQuery进行查询:

  • ToEntityArray()——返回所选实体的数组
  • ToComponentDataArray< T >——返回所选实体中类型T的组件的数组
  • CreateArchetypeChunkArray()——返回包含所选实体的所有块(由于查询对archetypes、shared component values和change filters的作用对于区块中的所有实体都是相同的,因此存储在返回块中的返回值与ToEntityArray()的返回值完全相同)
6.4.1、在Jobs中使用

在JobComponentSystem中,将EntityQuery对象传递给系统的Schedule()方法,举个例子,在HelloCube的IJobChunk示例中,m_Group参数是EntityQuery对象

// OnUpdate runs on the main thread.
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
    var rotationType = GetArchetypeChunkComponentType<Rotation>(false); 
    var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true);

    var job = new RotationSpeedJob()
    {
        RotationType = rotationType,
        RotationSpeedType = rotationSpeedType,
        DeltaTime = Time.deltaTime
    };

    return job.Schedule(m_Group, inputDependencies);
}

EntityQuery在内部使用Jobs来创建所需的数组,当将组传递给Schedule()方法时,EntityQuery Jobs就会利用并行处理将系统中自己的Jobs同时调度

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值