ECS进阶:FixedTimestepWorkaround
基于Unity2019最新ECS架构开发MMO游戏笔记7
- 官方ECS进阶案例解析
- 开始之前的准备工作:
- ECS进阶:FixedTimestepWorkaround
- 小结
- DOTS 逻辑图表
- 更新计划
- 作者的话
- 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主世界
官方ECS进阶案例解析
开始之前的准备工作:
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目录下找到Advanced/FixedTimestepWorkaround,并打开FixedTimestepWorkaround场景
ECS进阶:FixedTimestepWorkaround
之前学习的7个HelloCube小案例只是ECS入门,比较简单,现在的进阶篇会复杂一些了:
如上图所示,这个案例采用的双主角模式,让我想起了电影《无双》。这个VariableSpawner可以看成发哥,FixedSpawner可以看成城哥,不要小看城哥了,虽然用了Fixed来修饰,让人误以为一层不变,然而却装配了一个Slider,使其在不同的时间步长上千变万化,这就像我们的大脑,表面上波澜不惊,实则已经波涛汹涌。
跑题了,我们先看VariableSpawner发哥好了,打开发哥生成器的Inspector窗口,我们发现上面挂载了ConvertToEntity脚本。这个ConvertToEntity可以看成是常用组件了,任何游戏对象上挂载就可以在运行的时候自动转化成实体。
我猜在新一代的Unity编辑器中,新建物体的时候,不再需要挂载ConvertToEntity脚本。在右键Create的时候,就会有Entity选项,直接创建实体。或者创建出来的所有东西都是默认以实体创建的,再也没有转化的过程。
届时我们将忽略实体和游戏对象的概念,回归组件式开发,ECS默默在底层运行。
再次跑题,发哥身上除了ConvertToEntity脚本外,还有一个VariableRateSpawnerAuthoring脚本,且看:
/// <summary>/// 参考案例6. SpawnFromEntity/// </summary>public class VariableRateSpawnerAuthoring : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs{public GameObject projectilePrefab;public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem){var spawnerData = new VariableRateSpawner{Prefab = conversionSystem.GetPrimaryEntity(projectilePrefab),};dstManager.AddComponentData(entity, spawnerData);}public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs){referencedPrefabs.Add(projectilePrefab);}}
这个脚本和案例6. SpawnFromEntity的实体写法一样,先声明自己的预设,然后将自身转化成实体,最后在System中利用预设实体来不断生成新的实体。
/// <summary>/// 组件,只负责存储数据/// </summary>public struct VariableRateSpawner : IComponentData{public Entity Prefab;}/// <summary>/// 波动生成系统/// </summary>public class VariableRateSpawnerSystem : ComponentSystem{protected override void OnUpdate(){//遍历所有实体,发送更新命令,在更新命令中为其添加组件,并把数据交给组件储存Entities.ForEach((Entity spawnerEntity, ref VariableRateSpawner spawnerData, ref Translation translation) =>{var spawnTime = Time.timeSinceLevelLoad;var newEntity = PostUpdateCommands.Instantiate(spawnerData.Prefab);PostUpdateCommands.AddComponent(newEntity, new Parent {Value = spawnerEntity});PostUpdateCommands.AddComponent(newEntity, new LocalToParent());PostUpdateCommands.SetComponent(newEntity, new Translation {Value = new float3(0, 0.3f * math.sin(5.0f * spawnTime), 0)});PostUpdateCommands.SetComponent(newEntity, new ProjectileSpawnTime{SpawnTime = spawnTime});});}}
这里把组件和系统合在一起写了,毕竟组件就储存一条信息而已,实在没有必要额外弄一个脚本。
这里学到一个新的功能类PostUpdateCommands,它负责将更新命令发送给处理系统,这些更新命令会按照权重缓存起来,然后在主线程中排队执行。这里使用Instantiate来实例化实体,AddComponent来添加组件,SetComponent来传递数据。
这里值得一体的是System用到的几个组件:
/// <summary>
/// 类似原来的Transform组件
/// </summary>
namespace Unity.Transforms
{/// <summary>/// 父组件,只储存了实体的父实体/// </summary>[Serializable][WriteGroup(typeof(LocalToWorld))]public struct Parent : IComponentData{public Entity Value;}/// <summary>/// 前任父组件,储存上一任父实体/// </summary>[Serializable]public struct PreviousParent : ISystemStateComponentData{public Entity Value;}/// <summary>/// 子实体/// </summary>[Serializable][InternalBufferCapacity(8)][WriteGroup(typeof(ParentScaleInverse))]public struct Child : ISystemStateBufferElementData{public Entity Value;}}/// <summary>/// 发射体的生成时间/// </summary>[Serializable]public struct ProjectileSpawnTime : IComponentData{public float SpawnTime;}/// <summary>/// 相对于父实体的本地坐标/// </summary>[Serializable][WriteGroup(typeof(LocalToWorld))]public struct LocalToParent : IComponentData{public float4x4 Value;public float3 Right => new float3(Value.c0.x, Value.c0.y, Value.c0.z);public float3 Up => new float3(Value.c1.x, Value.c1.y, Value.c1.z);public float3 Forward => new float3(Value.c2.x, Value.c2.y, Value.c2.z);public float3 Position => new float3(Value.c3.x, Value.c3.y, Value.c3.z);}
这几个实体组件,都继承了组件相关的空接口,作用只是为了表明自己是什么样的组件,方便系统在使用时刷选。
在波动生成系统中不断发射出来的动感光波,其实就是ProjectileSpawnTimeAuthoring实体,其本身就是实体了,并不需要转化,只是需要把生成时间数据交给ProjectileSpawnTime 组件保存,这里姑且把两者放到一起:
/// <summary>/// 发射体的生成时间组件/// </summary>[Serializable]public struct ProjectileSpawnTime : IComponentData{public float SpawnTime;}/// <summary>/// 发射体生成时间设置/// </summary>[RequiresEntityConversion]public class ProjectileSpawnTimeAuthoring : MonoBehaviour, IConvertGameObjectToEntity{public float SpawnTime;public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem){var data = new ProjectileSpawnTime { SpawnTime = SpawnTime };dstManager.AddComponentData(entity, data);}}
剩下一个MoveProjectilesSystem 的脚本:
/// <summary>/// 发射体移动系统,负责将发射体不断向前移动,并在超出寿命后摧毁实体/// </summary>public class MoveProjectilesSystem : JobComponentSystem{[BurstCompile]struct MoveProjectileJob : IJobForEachWithEntity<ProjectileSpawnTime, Translation>{public EntityCommandBuffer.Concurrent Commands;public float TimeSinceLoad;public float ProjectileSpeed;public void Execute(Entity entity, int index, [ReadOnly] ref ProjectileSpawnTime spawnTime, ref Translation translation){float aliveTime = (TimeSinceLoad - spawnTime.SpawnTime);if (aliveTime > 5.0f){Commands.DestroyEntity(index, entity);}translation.Value.x = aliveTime * ProjectileSpeed;}}BeginSimulationEntityCommandBufferSystem m_beginSimEcbSystem;protected override void OnCreate(){m_beginSimEcbSystem = World.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();}protected override JobHandle OnUpdate(JobHandle inputDependencies){var jobHandle = new MoveProjectileJob(){Commands = m_beginSimEcbSystem.CreateCommandBuffer().ToConcurrent(),TimeSinceLoad = Time.timeSinceLevelLoad,ProjectileSpeed = 5.0f,}.Schedule(this, inputDependencies);m_beginSimEcbSystem.AddJobHandleForProducer(jobHandle);return jobHandle;}}
这个系统类似于奥特曼的动感光波发射器,很简单。
而城哥FixedSpawner和发哥发射动感光波的做法是一样的,只是改了个名字而已,你可以认为他们俩其实是一个人。就像电影《无双》里面的情节一样,发哥是城哥臆想出来的一个人物,发哥干了什么好事坏事,城哥也干了。
城哥与发哥不一样的地方在于,城哥还有一个厉害的姘头FixedTimestepUpdater,这个姘头获取了滑动条Slider的数据,用来改变FixedUpdate的更新速率,然后在FixedUpdate中调用其生成系统的Update,最终达到操纵动感光波发射时间的目的。
姘头并没有实际改变实体的组件数据和发射系统,改变的只是更新时间,例如原来是0.02秒更新一次,通过改变Time.fixedDeltaTime的值,就可以改变FixedUpdate的更新速率,如下图所示:
小结
这里其实有三套ECS,首先是发射体Projectile,它的整个系统都被后者利用;然后是发哥VariableSpawner和城哥FixedSpawner,他们俩都是发射动感光波,也就是不断生成发射体;最后是城哥的姘头FixedTimestepUpdater,姘头其实向我们展示了如何在MonoBehaviour中操作ECS的系统。
Projectile:
ECS | Scripts | Interface1 | Interface2 |
---|---|---|---|
Entity | ProjectileSpawnTimeAuthoring | IConvertGameObjectToEntity | |
Component | ProjectileSpawnTime | IComponentData | |
System | MoveProjectilesSystem | JobComponentSystem |
VariableSpawner:
ECS | Scripts | Interface1 | Interface2 |
---|---|---|---|
Entity | VariableRateSpawnerAuthoring | IConvertGameObjectToEntity | IDeclareReferencedPrefabs |
Component | VariableRateSpawner | IComponentData | |
System | VariableRateSpawnerSystem | ComponentSystem |
FixedSpawner:
ECS | Scripts | Interface1 | Interface2 |
---|---|---|---|
Entity | FixedRateSpawnerAuthoring | IConvertGameObjectToEntity | IDeclareReferencedPrefabs |
Component | FixedRateSpawner | IComponentData | |
System | FixedRateSpawnerSystem | ComponentSystem |
这里值得注意的是城哥和发哥的生成系统继承的是ComponentSystem,而非发射体系统继承的JobComponentSystem,JobComponentSystem利用了Jobs和Burst编译器,ComponentSystem显然并没有,而且可以受姘头的影响。
DOTS 逻辑图表
Spawn流程大体如下:
DOTS系统:
无双:
我是有多无聊,在这里演示无双的剧情,无双和此案例真的没有任何关联,这一篇笔记纯属臆想。
更新计划
作者的话
如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟: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主世界
ECS进阶:FixedTimestepWorkaround相关推荐
- 阿里云ECS进阶训练营Day1 搭建VuePress
文章目录 零.Demo地址 一.开通阿里云服务器 1.1 购买服务器 1.1.1 去哪购买? 1.1.2 如何购买? 1.2 配置服务器 1.2.1 需要的信息 1.2.2 配置安全组 二.VuePr ...
- ECS进阶Day03:SLB负载均衡实践
SLB负载均衡实践 云服务器状态确认 负载均衡配置 在 **协议&监听** 页面,输入如下信息,完成后,点击 **下一步** . 负载均衡(Server Load Balancer)是将访问流 ...
- 【阿里云ECS进阶训练营】day07基于ECS和NAS搭建个人网盘
目录 一:背景知识 二:实验步骤 1.连接ECS服务器 2.安装OwnCloud 3.安装Apache服务 4.安装并配置PHP 5.配置OwnCloud 6.挂载NAS服务 一:背景知识 云服务器E ...
- 用ECS做HexMap:鼠标点击六边形单元涂色
基于Unity2019最新ECS架构开发MMO游戏笔记21 准备工作 鼠标触碰六边形单元 鼠标点击位置 更新计划 作者的话 ECS系列目录 ECS官方示例1:ForEach ECS官方案例2:IJob ...
- 用ECS做HexMap:利用RenderMesh为六边形涂色
基于Unity2019最新ECS架构开发MMO游戏笔记18 为六边形涂色 链接相邻单元 颜色混合 更新计划 作者的话 ECS系列目录 ECS官方示例1:ForEach ECS官方案例2:IJobFor ...
- 用ECS做HexMap:自动生成地图系统
基于Unity2019最新ECS架构开发MMO游戏笔记16 自动生成地图系统 AutoCreateMapSystem 神奇的六边形 六边形实体 创建者和创建六边形单元系统 更新计划 作者的话 ECS系 ...
- 用ECS做HexMap:六边形单元的颜色混合
基于Unity2019最新ECS架构开发MMO游戏笔记19 颜色混合 颜色平均化 区域混合 三角化混合区域 边界连接桥 填充间隙 边界合并 更新计划 作者的话 ECS系列目录 ECS官方示例1:For ...
- 用ECS做HexMap:重构地图系统
基于Unity2019最新ECS架构开发MMO游戏笔记20 概述 概念 原型Archetypes 内存块 实体查询EntityQuery 任务Jobs 系统组织 优化地图系统 主世界 六边形单元生成系 ...
- 2021第一场 | 阿里云高校计划训练营全面升级!0成本体验云计算入门到进阶
简介:2021,走进云计算的美妙世界(参加训练营免费获取ACA考试资格) 近年来云计算越来越受到重用,它不再仅仅是开源发烧友们的选择,已经在多方面得到了价值体现. 甚至网上流传一句话:云计算适合零基础 ...
最新文章
- JVM的内存区域划分(转)
- WebService Software Factory 设计草图
- php视频降清晰度,使用FFMPEG降低视频分辨率
- python第6天作业
- dos2unix,unix2dos
- 谷粒商城:06. 前端开发基础知识
- java实验十三io_Java语言基础13—IO
- Photoshop实例视频教程
- 项目实战第二十一讲:平台商品库
- linux能远程开机么,Linux下如何实现远程开机
- 融资租赁、直租回租傻傻分不清楚
- JS实现小方块上下移动
- Selective Search(选择性搜索)算法学习
- iCoremail企业邮箱移动办公新突破
- 基于SSM框架的音乐网站系统设计与实现
- CAD中插入外部参照字体会变繁体_CAD快速入门(二十七):外部参照
- 晴天小猪历险记之Hill
- 导出excel文件 java_java实现导出excel文件
- 犀牛插件开发-创建圆-Python-点后周长构建圆-Rhino插件
- win7电脑关机出现蓝屏怎么办?解决win7电脑关机蓝屏的方法