您当前的位置: 首页 > 

CloudHu1989

暂无认证

  • 11浏览

    0关注

    89博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

ECS官方示例1. ForEach

CloudHu1989 发布时间:2019-08-10 08:22:54 ,浏览量:11

基于Unity2019最新ECS架构开发MMO游戏笔记0
  • 关于ECS
    • 新的改变
  • 代码示例
    • 1. ForEach
    • 小结
      • ECS 逻辑图表
  • 更新计划
    • 作者的话
  • 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

关于 ECS 我想大家都不陌生了,比较Unity已经强推这么久了,如果你还不知道的话,的确有点孤陋寡闻了(其实我也是才知道的LOL)。我是因为看到MegaCity的视频才了解到ECS的,当时被庞大复杂的场景震撼到了,一直以来Unity官方出的视频都带给我强大的视觉冲击。第一部让我自发宣传的是《Adam》,后面连续出了续集2和3,然后戛然而止,却意犹未尽。Unity不开发游戏真是太可惜了,这样好的创意和功力,无论做什么都会让人拍手称赞的。至于最新的《异教徒》,让我不知道说什么好了,真希望赶快放续集,这个视频创意比亚当三部曲还要赞,千万别只出三部啊! 跑题了,我是发散性思维,超级喜欢跑题。话说回来,ECS虽然现在还没有完善,但是真是值得期待。我陆陆续续在网上找了很多相关的知识来看,越看越觉得有意思,不知不觉中越陷越深了,原本只是看了一个视频而已。 我理解的ECS是相对于OOP的,OOP就是Object Oriented Program啦,面向对象编程,格言就是Everything is Object,一切都是对象,这种设计其实是来源于现实世界的,道法自然嘛,整个虚拟世界都是在模仿现实世界,编程思维和设计也不例外。 面向对象老生常谈了,我觉得没有必要多讲了,就说说ECS吧,E是Entity实体,相对于Object对象的存在;C是Component组件,其实我认为用Data来表达更好一些,C代表的就是数据;S是System系统,相对于原来的Controller控制器。 总体的意思就是用数据来驱动实体的系统,可以这么理解,反正我是这么狭隘的认为的。 ECS相对于OOP有什么优势吗? 国外的大佬写了教学和性能比对的文章Unity* 实体组件系统 (ECS)、C# 作业系统和突发编译器入门水木本源大佬转了过来,看了实在受益匪浅。 说白了,ECS比OOP更快,而且不是一般的快,在大量测试的情况下快了上百倍。

新的改变

我总结了ECS带来的一些改变:

  1. 全新的编程设计 ,原来所有的都是对象,现在所有都是实体,我们原来是用控制器来操控对象,现在用System来操控Component里的数据,而数据通过实体表现出来;
  2. 大数据将以 大量实体 进行展示,因为速度的突破,不用担心渲染跟不上了,万人同屏不卡顿;
  3. 迎接 新一代引擎 架构,与ECS相伴的还有Jobs(C#任务系统),Burst编译器,三者构成DOTS(新的游戏开发架构);
  4. 全新的 编辑器 ,Unity的编辑器会重做,原来那套OOP的编辑器是看不见Entity的,毕竟已经不是对象了,至于做成什么样子,值得期待;
  5. 新的物理引擎Havok Physics ,Unity为了ECS还特别收购了这家公司;
  6. 看不见的 ECS底层架构 ,我们现在虽然已经可以使用了,但是底层还在完善,利用 DOTS Data-Oriented Tech Stack ,面向数据的堆栈技术,我们会跑得更快;
代码示例

啰里啰唆了这么多,不如直接上代码来的直观,前戏虽有必要,但是我的大刀早已饥渴难耐! 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/1. ForEach,并打开ForEach场景

1. ForEach

场景里总共有四个游戏对象,呃,等下就不是那么回事儿了!一切都源于一个神奇的脚本,我们来找到它:

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

打开RotatingCube上挂的ConvertToEntity脚本,这就是我们要找的神奇脚本,它将Unity的游戏对象GameObject转化成Entity,从而让游戏运行更加高效,详细的原理看一开始推荐国外大佬那篇文章。 下面我们来看看ConvertToEntity脚本是如何完成这项工作的:

    /// 
    /// 将游戏对象转化成ECS的实体Entity
    /// 
    [DisallowMultipleComponent]//不允许多组件
    public class ConvertToEntity : MonoBehaviour
    {
        /// 
        /// 转化模式枚举
        /// 
        public enum Mode
        {
            ConvertAndDestroy,//转化并摧毁,该模式下GameObject在被转化成实体Entity后原游戏对象被摧毁
            ConvertAndInjectGameObject//转化并注入游戏对象,这个模式不会摧毁原来的游戏对象
        }
        /// 
        /// 转化模式
        /// 
        public Mode ConversionMode;
        
        void Awake()
        {
            if (World.Active != null)//这里新增了世界的概念,目的是划分OOP世界和ECS世界,游戏对象只存在于面向对象的世界中,实体同理
            {
                // Root ConvertToEntity is responsible for converting the whole hierarchy
                //根节点的转化脚本负责转化整个层级,举个栗子:这个脚本挂在父节点RotatingCube上,那么子节点ChildCube也会被转化成实体
                if (transform.parent != null && transform.parent.GetComponentInParent() != null)
                    return;//一个层级中只能在根节点上挂这个脚本,否则会在这里返回出去
                //根据不同的模式调用不同的方法
                if (ConversionMode == Mode.ConvertAndDestroy)
                    ConvertHierarchy(gameObject);
                else
                    ConvertAndInjectOriginal(gameObject);
            }
            else
            {
                UnityEngine.Debug.LogWarning("ConvertEntity failed because there was no Active World", this);
            }
        }
        /// 
        /// 注入原始组件
        /// 
        /// 实体管理器
        /// 实体
        /// 变化组件
        static void InjectOriginalComponents(EntityManager entityManager, Entity entity, Transform transform)
        {
            foreach (var com in transform.GetComponents())
            {//这里遍历所有组件,并将实体注入到原始组件中,详询底层的代码
                if (com is GameObjectEntity || com is ConvertToEntity || com is ComponentDataProxyBase)
                    continue;
                //我们可以不关心更深层次的实现,只需了解这个表层的脚本功能即可,需要使用的时候把这个脚本当作组件来用
                entityManager.AddComponentObject(entity, com);
            }
        }
        /// 
        /// 添加递归,把根节点下所有子节点都添加到实体管理器中
        /// 
        /// 实体管理器
        /// 变化组件
        public static void AddRecurse(EntityManager manager, Transform transform)
        {
            GameObjectEntity.AddToEntityManager(manager, transform.gameObject);
            
            var convert = transform.GetComponent();
            if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
                return;
                
            foreach (Transform child in transform)
                AddRecurse(manager, child);
        }
        /// 
        /// 注入原始组件
        /// 
        /// 源游戏对象世界
        /// 模拟世界,实体管理器所操控的实体世界
        /// 变化组件
        /// 循环递归直到层级中的所有对象都注入完,True:根节点
        public static bool InjectOriginalComponents(World srcGameObjectWorld, EntityManager simulationWorld, Transform transform)
        {
            var convert = transform.GetComponent();

            if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
            {
                var entity = GameObjectConversionUtility.GameObjectToConvertedEntity(srcGameObjectWorld, transform.gameObject);
                InjectOriginalComponents(simulationWorld, entity, transform);
                transform.parent = null;
                return true;
            }
            
            for (int i = 0; i 
        {
            var deltaTime = Time.deltaTime;
            rotation.Value = math.mul(math.normalize(rotation.Value),
                quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime));
        });
    }
}
小结

我们来看看这个小案例是如何实现ECS架构的:

ECSScripts继承EntityRotationSpeedAuthoring_ForEachIConvertGameObjectToEntityComponentRotationSpeed_ForEachIComponentDataSystemRotationSpeedSystem_ForEachComponentSystem

通过上面这个对照表是不是理清开发思路了?下面通过逻辑图表来看看解耦的模式:

ECS 逻辑图表
Entity Component System 你好!Component, 数据交给你储存? 数据给你操控,System? 转起来,谢谢! 我不想转,谢谢! Entity转了很长时间 …… …… Entity Component System

我大概是这么理解的,建议把Component换成Data,更容易看懂。:

Data
Entity
Component
System
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主世界

什么是ECS

关注
打赏
1664096582
查看更多评论
立即登录/注册

微信扫码登录

0.0865s