ECS官方案例3. IJobChunk

31 篇文章 3 订阅

官方案例解析3

开始之前的准备工作:
0下载Unity编辑器(2019.1.0f1 or 更新的版本),if(已经下载了)continue;
1下载官方案例,打开Git Shell输入:
git clone https://github.com/Unity-Technologies/EntityComponentSystemSamples.git --recurse
or 点击Unity官方ECS示例下载代码
if(已经下载了)continue;
2用Unity Hub打开官方的项目:ECSSamples
3在Assets目录下找到HelloCube/3. IJobChunk ,并打开IJobChunk 场景

3. IJobChunk

这个案例演示了基于Jobs的ECS系统如何旋转一对方块儿,与同样使用Jobs系统的案例2不同的是,在循环遍历的时候,前者迭代的是实体,而这个案例迭代的是块(内存块,这个块是被一开始基于相同原型分配好了的,也就是说,如果这20个实体有相同的组件,那么他们会被紧密的安排在一块内存中,从而方便处理器进行操作。这是相对于面向对象的散列内存而言的,在面向对象中要操作内存中的某个对象时,你不得不在整个内存中寻找它,这样会降低读取速度,也不方便批量操作。ECS则会非常紧密地分配实体在内存中的位置,相同的组件会被统一放在块中,读取速度快,也方便批量操作)。下面来一探究竟:

  • Main Camera ……主摄像机
  • Directional Light……光源
  • RotatingCube……旋转的方块
    • ChildCube……子方块

和案例2一样RotatingCube上同样挂了ConvertToEntity脚本,它将Unity的游戏对象GameObject转化成Entity,从而让游戏运行更加高效,脚本的工作原理已经在上一篇讲过了,此处跳过。
RotatingCube上还挂了另外一个脚本RotationSpeedAuthoring_IJobChunk,下面我们来看一下这个脚本:

[RequiresEntityConversion]
public class RotationSpeedAuthoring_IJobChunk : MonoBehaviour, IConvertGameObjectToEntity
{
    public float DegreesPerSecond = 360.0F;
    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        var data = new RotationSpeed_IJobChunk { RadiansPerSecond = math.radians(DegreesPerSecond) };
        dstManager.AddComponentData(entity, data);
    }
}

代码和上一篇的脚本RotationSpeedAuthoring_IJobForEach几乎一毛一样,区别在于Chunk关键字上了,我这里略过储存数据的RotationSpeed_IJobChunk脚本不讲,实在没有什么可说的,就储存了一个数据而已:

/// <summary>
/// 我啥也不干,就放数据
/// </summary>
[Serializable]
public struct RotationSpeed_IJobChunk : IComponentData
{
    public float RadiansPerSecond;
}

这里的IComponentData上次讲过,它就是一个空接口而已,作用只是表明自己是Component的身份,从而让System识别,非常纯粹的一个脚本。这里我们将理解到表明身份的重要性,下面是这个案例的重点脚本RotationSpeedSystem_IJobChunk:

/// <summary>
/// 按块操作实体系统
/// </summary>
public class RotationSpeedSystem_IJobChunk : JobComponentSystem
{
    EntityQuery m_Group;//查询到特定组件的实体,将其放入这个组中

    /// <summary>
    /// 这里根据类型来查询到特定的实体
    /// </summary>
    protected override void OnCreate()
    {
        // Cached access to a set of ComponentData based on a specific query
        ///typeof(Rotation)=带有Rotation组件的;ComponentType=对应RotationSpeed_IJobChunk组件类型的
        /// ReadOnly=只读会加快获取实体的速度,ReadWrite=读写 则相对较慢
        m_Group = GetEntityQuery(typeof(Rotation), ComponentType.ReadOnly<RotationSpeed_IJobChunk>());
    }

    // Use the [BurstCompile] attribute to compile a job with Burst. You may see significant speed ups, so try it!
    [BurstCompile]//同样使用Burst编译器来加速,区别是使用了块接口:IJobChunk
    struct RotationSpeedJob : IJobChunk
    {
        public float DeltaTime;
        /// <summary>
        /// 原型块组件类型=Rotation
        /// </summary>
        public ArchetypeChunkComponentType<Rotation> RotationType;
        /// <summary>
        /// 只读 原型块组件类型=RotationSpeed_IJobChunk
        /// </summary>
        [ReadOnly] public ArchetypeChunkComponentType<RotationSpeed_IJobChunk> RotationSpeedType;
        /// <summary>
        /// 找出满足条件的实体来执行
        /// </summary>
        /// <param name="chunk"><原型块/param>
        /// <param name="chunkIndex">块索引</param>
        /// <param name="firstEntityIndex">第一个实体索引</param>
        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            var chunkRotations = chunk.GetNativeArray(RotationType);
            var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);
            for (var i = 0; i < chunk.Count; i++)
            {
                var rotation = chunkRotations[i];
                var rotationSpeed = chunkRotationSpeeds[i];

                // Rotate something about its up vector at the speed given by RotationSpeed_IJobChunk.
                chunkRotations[i] = new Rotation
                {
                    Value = math.mul(math.normalize(rotation.Value),
                        quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))
                };
            }
        }
    }

    // OnUpdate runs on the main thread.
    /// <summary>
    /// 这个方法在主线程上运行
    /// </summary>
    /// <param name="inputDependencies">输入依赖</param>
    /// <returns></returns>
    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        // Explicitly declare: 声明
        // - Read-Write access to Rotation 读写的方式访问旋转
        // - Read-Only access to RotationSpeed_IJobChunk 只读的方式访问旋转速度
        var rotationType = GetArchetypeChunkComponentType<Rotation>();
        var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed_IJobChunk>(true);

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

        return job.Schedule(m_Group, inputDependencies);
    }
}

思路是明确的,一个案例比之前一个更快,切更有目的性!
这就是代码的优化迭代!

小结

我们来对比案例二:

ECSScriptsInherit
EntityRotationSpeedAuthoring_IJobForEachIConvertGameObjectToEntity
ComponentRotationSpeed_IJobForEachIComponentData
SystemRotationSpeedSystem_IJobForEachJobComponentSystem

和案例三:

ECSScriptsInherit
EntityRotationSpeedAuthoring_IJobChunkIConvertGameObjectToEntity
ComponentRotationSpeed_IJobChunkIComponentData
SystemRotationSpeedSystem_IJobChunkJobComponentSystem

变化不大,System都利用了Jobs和Burst编译器,Chunk会跑得更快!

DOTS 逻辑图表

System Burst Jobs Entities 你好!任务交给你快速编译一下? 编译好了,任务交给你安排执行? 不好意思,你们的旋转已经排上日程了! 全体都有,集合,向右看齐,向前看,转起来! 我们不想转,我们要放假! 又转起来了! 为什么会说“又”? 旋转,跳跃 我闭着眼…… System Burst Jobs Entities

流程大体如下:

Data
Schedule
OnUpdate
Entities
Component
System
Burst
Jobs
ForEach

更新计划

Mon 12 Mon 19 Mon 26 1. ForEach 2. IJobForEach 3. IJobChunk 4. SubScene 5. SpawnFromMonoBehaviour 6. SpawnFromEntity 7. SpawnAndRemove 休息 修正更新计划 参加表哥婚礼 进阶:FixedTimestepWorkaround 进阶:BoidExample 进阶:SceneSwitcher 我是休息时间 资源整合 部署服务器 启动流程 登录流程 游戏主世界 待计划 待计划 待计划 待计划 待计划 我是休息时间 待计划 待计划 待计划 待计划 待计划 我是休息时间 读取Excel自动生成Entity 读取Excel自动生成Component 读取数据库自动生成Entity 读取数据库自动生成Component ESC LuaFrameWork Skynet DOTS 官方示例学习笔记 -----休息----- 基于ECS架构开发MMO学习笔记 LuaFrameWork学习笔记 -----休息----- 基于Skynet架构开发服务器学习笔记 制作代码自动生成工具 总结 基于Unity2019最新ECS架构开发MMO游戏笔记

作者的话

AltAlt

如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤!
当然,不需要技术支持也欢迎加入进来,随时可以请我喝咖啡、茶和果汁!( ̄┰ ̄*)

ECS系列目录

ECS官方示例1:ForEach

ECS官方案例2:IJobForEach

ECS官方案例3:IJobChunk

ECS官方案例4:SubScene

ECS官方案例5:SpawnFromMonoBehaviour

ECS官方案例6:SpawnFromEntity

ECS官方案例7:SpawnAndRemove

ECS进阶:FixedTimestepWorkaround

ECS进阶:Boids

ECS进阶:场景切换器

ECS进阶:MegaCity0

ECS进阶:MegaCity1

UnityMMO资源整合&服务器部署

UnityMMO选人流程

UnityMMO主世界

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的 Unity ECS 架构案例: 假设你要实现一个简单的游戏场景,其中有多个球体以及一个玩家操控的方块。每个球体都有一个初始速度和一个随机的颜色,当玩家的方块与球体碰撞时,球体将改变颜色并反弹。 首先,你需要定义组件,比如球体组件和方块组件: ``` public struct BallComponent : IComponentData { public float3 velocity; public float3 color; } public struct BlockComponent : IComponentData { public float3 position; } ``` 然后,你需要为球体和方块添加相应的组件,并将它们添加到 ECS 实体中: ``` Entity block = manager.CreateEntity(); manager.AddComponentData(block, new BlockComponent { position = new float3(0, 0, 0) }); for (int i = 0; i < 10; i++) { Entity ball = manager.CreateEntity(); manager.AddComponentData(ball, new BallComponent { velocity = UnityEngine.Random.onUnitSphere * 3f, color = new float3(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value) }); } ``` 接下来,你需要定义系统来控制球体的运动和碰撞检测。这里我们可以使用 Unity 的 Job System 和 Burst Compiler 来提高性能: ``` public struct BallMovementJob : IJobForEach<BallComponent> { public float deltaTime; public void Execute(ref BallComponent ball) { ball.velocity += new float3(0, -9.81f, 0) * deltaTime; ball.color = math.lerp(ball.color, new float3(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value), deltaTime / 10f); } } public struct BallCollisionJob : IJobForEach<BlockComponent, BallComponent> { public void Execute(ref BlockComponent block, ref BallComponent ball) { if (math.distance(block.position, ball.position) < 1f) { ball.color = new float3(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); ball.velocity = math.reflect(ball.velocity, math.normalize(block.position - ball.position)); } } } protected override JobHandle OnUpdate(JobHandle inputDeps) { var moveJob = new BallMovementJob { deltaTime = Time.deltaTime }; var moveHandle = moveJob.Schedule(this, inputDeps); var collisionJob = new BallCollisionJob { }; var collisionHandle = collisionJob.Schedule(this, moveHandle); return collisionHandle; } ``` 最后,你需要在场景中添加一个空物体并将 `EntityManager` 组件和 `Convert To Entity` 组件添加到它上面。然后将上述代码添加到脚本中,你的 ECS 实现就完成了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CloudHu1989

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值