EntitiesSample_6. Reparenting

该示例的关键点有两个,一个是 Parent,Child组件的使用,这两个组件是内置的,标识对象之间嵌套的引用,第二个关键点是 ecb.Playback(state.EntityManager)的使用,在使用EntityCommandBuffer对实体惊醒操作之后,使用playback接口保证数据的同步,EntityCommandBuffer命令执行在各个job中,无法确定执行的顺序和时间,就像是Mono托管编程中的协程,通过回调函数与主线程数据同步
public partial struct ReparentingSystem : ISystem
    {
        bool attached;
        float timer;
        const float interval = 0.7f;

        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
            timer = interval;
            attached = true;
            state.RequireForUpdate<ExecuteReparenting>();
            state.RequireForUpdate<RotationSpeed>();
        }

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            //时间计时 interval,interval秒设置一次
            timer -= SystemAPI.Time.DeltaTime;
            if (timer > 0)
            {
                return;
            }
            timer = interval;
            //获取场景中RotationSpeed挂载这个脚本的实体
            var rotatorEntity = SystemAPI.GetSingletonEntity<RotationSpeed>();
            //创建ecb命令,使用该命令对实体世界操作,这个命令可以在各个job上调度
            var ecb = new EntityCommandBuffer(Allocator.Temp);

            if (attached)
            {
                //获取挂载RotationSpeed脚本的子实体,如果一个实体有子实体,
                //会自动被挂载上Child组件,引用的所有的子实体,Child继承自IBufferElementData,就像mono中的getComponentsInChild
                //子实体拥有一个Parent组件,引用的父级
                DynamicBuffer<Child> children = SystemAPI.GetBuffer<Child>(rotatorEntity);
                for (int i = 0; i < children.Length; i++)
                {
                    // Using an ECB is the best option here because calling EntityManager.RemoveComponent()
                    // instead would invalidate the DynamicBuffer, meaning we'd have to re-retrieve
                    // the DynamicBuffer after every EntityManager.RemoveComponent() call.
                    使用ECB是最好的选择,因为调用EntityManager。移除组件()
                    //相反,会使DynamicBuffer无效,这意味着我们必须重新检索
                    //每个EntityManager之后的DynamicBuffer。RemoveComponent()调用。
                    ecb.RemoveComponent<Parent>(children[i].Value);
                }
                /* 这个是可以替代上面的循环,RemoveComponent可以传一个实体数组,
                 * 参数就是把 DynamicBuffer<Child> 先转化成 NativeArray<Child> 然后在转化成 NativeArray<Entity> 
                ecb.RemoveComponent<Parent>(children.AsNativeArray().Reinterpret<Entity>());
                */
            }
            else
            {
                //查找没有 RotationSpeed组件的实体,然后添加Parent组件
                foreach (var (transform, entity) in
                         SystemAPI.Query<RefRO<LocalTransform>>()
                             .WithNone<RotationSpeed>()
                             .WithEntityAccess())
                {
                    ecb.AddComponent(entity, new Parent { Value = rotatorEntity });
                }
                /* 替换上面的循环
                var query = SystemAPI.QueryBuilder().WithAll<LocalTransform>().WithNone<RotationSpeed>().Build();
                ecb.AddComponent(query, new Parent { Value = rotatorEntity });
                */
            }
            //ecb的操作结果传到管理器中
            ecb.Playback(state.EntityManager);

            attached = !attached;
        }
    }

其中有两个循环的简写

//删除数组中所有实体Parent组件
ecb.RemoveComponent<Parent>(children.AsNativeArray().Reinterpret<Entity>());
//创建一个查询,给这个查询结果添加 Parent组件
var query = SystemAPI.QueryBuilder().WithAll<LocalTransform>().WithNone<RotationSpeed>().Build();
ecb.AddComponent(query, new Parent { Value = rotatorEntity });