笨木头  2019-12-05 14:28     ECS入门     阅读(10994)     评论(4)
转载请注明,原文地址: http://www.benmutou.com/archives/2823
文章来源:笨木头与游戏开发

------------ 以下是废话

唔,可能我这个教程的目录看起来有点糟糕,偶尔讲一下筛选数据,偶尔又讲一下概念,偶尔又讲一下创建实体的方式。

 

这不,现在突然又跳到什么System执行顺序了。

 

其实是这样的,我在写这些教程之前,对ECS也是一窍不通的。

我更多的是按照官方示例走下去,在走的过程中,会发现很多不懂的点。

 

所以,我的教程顺序看起来会非常乱,但其实,这更符合一个完全不懂ECS的人的学习进程。

按照我的教程顺序,你会发现自己学到的都是刚好需要、刚好不懂的,前一篇学过的刚好就用在下一篇。

这样大家学起来可能会轻松一点,教程顺序虽然很乱,但大家的心千万不要乱。

 

ECS要做的事情就是——写组件、创建实体、写系统处理逻辑,只不过在这过程中为了解决某些特定的问题,我们需要引入各种用法和概念。

现在最重要的是入门,入门之后就可以抱着官方手册自己浪了。

 

好了,我们继续吧。

------------ 废话结束



1.逻辑的先后顺序

我们已经知道,System的OnUpdate函数会在每一帧自动被调用。

 

唔,实际上,更准确的,应该分两种情况:

ComponentSystem的OnUpdate:每帧都会被调用。

JobComponentSystem的OnUpdate:取决于Job里筛选出来的实体数量是否大于0,如果是,则每帧都会调用OnUpdate;如果筛选出来的实体数量是0,那么,OnUpdate不会被调用。

 

这个不太重要。

 

重要的是,我们的System是处理游戏逻辑,游戏逻辑肯定就会涉及到先后顺序。

比如好吧,我比不出来

 

再比如一下吧,比如玩家走到某个地方后,我才能做一些操作。假设【走动的逻辑】和【做一些操作的逻辑】分别在两个System里。那么,【走动的逻辑】的应该在【做一些操作的逻辑】前面执行。

 

这时候就涉及到先后顺序了,没错,为了解决这类问题,System的OnUpdate函数的执行是有先后顺序的。



2.系统分组(ComponentSystem Group

在了解系统如果控制先后顺序之前,需要先了解系统的分组。

 

唔,其实很简单,就是分组

 

比如,SystemA和SystemB都属于一个叫做SystemGroupHello的分组(名字无所谓),SystemC和SystemD属于SystemGroupOther分组。

就是这么简单,就是我们所认知的普通的分组概念。



3.System的执行顺序(System Update Order

好,接下来我要贴很长的一串东西,大家不要吓到:



InitializationSystemGroup(负责初始化工作的系统分组)

SimulationSystemGroup(负责逻辑运算的系统分组)

PresentationSystemGroup(负责图形与渲染工作的系统分组)

好吧,并没有什么很长的东西,我随便吓吓大家的。

 

以上三个是ECS默认的系统分组,很重要,这三个分组里的System从上到下按照顺序被执行。(实际上SystemGroup本身也属于System,也会被调用OnUpdate函数)。

(以上三个分组的中文翻译参考来源:https://connect.unity.com/p/unity-ecs-wu-liao-jie-systemzhi-xing-shun-xu

 

接下来又是一长串的东西,这次是真的:



  • InitializationSystemGroup
    • BeginInitializationEntityCommandBufferSystem
    • CopyInitialTransformFromGameObjectSystem
    • SubSceneLiveLinkSystem
    • SubSceneStreamingSystem
    • EndInitializationEntityCommandBufferSystem
  • SimulationSystemGroup
    • BeginSimulationEntityCommandBufferSystem
    • TransformSystemGroup
      • EndFrameParentSystem
      • CopyTransformFromGameObjectSystem
      • EndFrameTRSToLocalToWorldSystem
      • EndFrameTRSToLocalToParentSystem
      • EndFrameLocalToParentSystem
      • CopyTransformToGameObjectSystem
    • LateSimulationSystemGroup
    • EndSimulationEntityCommandBufferSystem
  • PresentationSystemGroup
    • BeginPresentationEntityCommandBufferSystem
    • CreateMissingRenderBoundsFromMeshRenderer
    • RenderingSystemBootstrap
    • RenderBoundsUpdateSystem
    • RenderMeshSystem
    • LODGroupSystemV1
    • LodRequirementsUpdateSystem
    • EndPresentationEntityCommandBufferSystem
    以上全部都是ECS自带的System,这是基于0.3.0版本,以后的版本可能会发生变化(实际上我发现有些System压根找不到,魂淡,是官方手册没有及时更新么)。

     

    先别抖,别害怕,注意粗体部分的内容:InitializationSystemGroup,SimulationSystemGroup,PresentationSystemGroup。

    这三个是我们刚刚提到的默认系统分组,实际当中,我们大部分人可能都会在默认的分组下搞事情。

     

    每个分组下有很多个System,而某些System下又可能包含子System。

    至于这些System有什么用,大家当然是先不用管了。

     

    以上贴出的所有System,都会按照罗列的顺序执行OnUpdate函数,如果System又包含了子System,那么就子System也会执行OnUpdate函数。

    这个有什么用呢?大家可以理解为"生命周期"(实际上官方没有提到这个东西),这些按顺序执行的System,会影响到我们的某些决策。

     

    比如,我们想在初始化之前搞事情,那就看看初始化的第一个System是谁,然后让我们自己的System在它之前执行。

    如果大家之前有看过Entity Debugger(Window->Analysis->Entity Debugger)的话,应该就会发现,上面列出的部分System就在Debugger里:



    Debugger里的System也是按照执行顺序逐一罗列的,未来我们新增的System也会出现在这里。

    4.控制System的执行顺序

    等等,怎么样可以让我们的System在别的System之前搞事情呢?

     

    我们来看看三个特性(Attribute)

    UpdateInGroup:指定当前System在哪个分组下

    UpdateBefore:指定当前System在哪个System之前执行

    UpdateAfter:指定当前System在哪个System之后执行

     

    我们直接看代码,随便创建一个场景,然后场景一个System类:
    using Unity.Entities;

    [UpdateBefore(typeof(SceneSystem))] public class MutouSystem : ComponentSystem { protected override void OnUpdate () { } }
     

    我创建了一个很简单的System,但是附加了UpdateBefore特性,并指定了SceneSystem类型。

    这样的话,我们的MutouSystem肯定就会在SceneSystem之前执行了。
  •  

    然后,什么都不用做,直接运行,然后看看Entity Debugger:



    怎么样,MutouSystem是不是出现在SceneSystem之前了

    没错才怪啊喂!竟然不是!(好丢人怎么办

     

    很奇怪,MutouSystem出现的地方和我们想象的不一样,这是为什么呢?

    因为爱情(旁白:闭嘴!)

     

    唔,很重要的一点,System的执行顺序首先是按照System Group排序的,比如InitializationSystemGroup一定是在SimulationSystemGroup之前执行,那么,InitializationSystemGroup下的所有System肯定也是在SimulationSystemGroup下所有System之前执行。

     

    有点绕,换句话说,先按分组排序,然后分组内部自己再排序,无论你内部怎么排序,都不可能跨越分组的排序。

     

    所以,我们要指定System的执行顺序,首先就得确认System所属的分组,然后System就在分组内部调整排序。

    如果我们不知道System所属分组,那么,它就会默认分配到SimulationSystemGroup分组下。

     

    所以,我们的MutouSystem是属于SimulationSystemGroup分组的,而这个分组下不存在SceneSystem,因此,我们的UpdateBefore特性是无效的。

    于是,改改代码:
    using Unity.Entities;

    [UpdateInGroup(typeof(InitializationSystemGroup))] [UpdateBefore(typeof(SceneSystem))] public class MutouSystem : ComponentSystem { protected override void OnUpdate () {} }
     

    我在MutouSystem顶部又加了一个UpdateInGroup特性,指定MutouSystem所属分组,于是,再次运行:



    好了,正常了。

    不过,MutouSystem和SceneSystem中间还隔了很多System,也就是说,UpdateBefore只能保证在某个System之前执行,而不能保证是严格地在某个System的上一个执行。

     

    我们再改改,新增一个System:
    [UpdateInGroup(typeof(InitializationSystemGroup))]
    [UpdateAfter(typeof(MutouSystem))]
    public class LoveSystem : ComponentSystem
    {
        protected override void OnUpdate () {}
    }
     

    这次我们指定LoveSystem在MutouSystem之后执行,运行看看效果:



    结果和我们预想一样。

     

     

    我们再改改,把MutouSystem放到SceneSystem之后执行:
    [UpdateInGroup(typeof(InitializationSystemGroup))]
    [UpdateAfter(typeof(SceneSystem))]
    public class MutouSystem : ComponentSystem
    {
        protected override void OnUpdate () {}
    }
     

    运行看看效果:



    好,依旧完美,LoveSystem永远跟在MutouSystem身后了。

    看,这就是我所说的爱情(旁白:滚!)

     

    好了,关于System的执行顺序,就介绍这么多吧。

     

    注意,本系列教程基于DOTS相关预览版的Package包,是预览版,不代表正式版的时候也适用。

    转发本系列文章的朋友请注意,带上原文链接和原文日期,避免误导未来使用正式版的开发者。

4 条评论
  • Miha 2020-02-27 21:02:19

    写的不错:)
    0回复
  • 日落之吻 2020-02-07 10:47:36

    博主写的挺好的,生动有趣,由浅入深!
    0回复
  • aladdin 2020-01-09 16:14:12

    请问木头兄,你用的是Unity2019.3运行官方的ECSSample的么,我用2019.2.3打开一堆报错
    0回复
    • 博主 笨木头 2020-01-09 20:14:45

      对,我用的是比较高的版本,最好看看官方的版本要求(每个版本的Entities都可能不一样,毕竟是预览版)
      0回复
发表评论
粤ICP备16043700号

本博客基于 BlazorAnt Design Blazor 开发