Unity3D中脚本的执行顺序和编译顺序

时间:2022-12-09 17:28:27

http://www.cnblogs.com/champ/p/execorder.html

在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行。与脚本有关的也就是编译和执行啦,本文就来研究一下Unity中脚本的编译和执行顺序的问题。

事件函数的执行顺序

先说一下执行顺序吧。 
官方给出的脚本中事件函数的执行顺序如下图: 
Unity3D中脚本的执行顺序和编译顺序

我们可以做一个小实验来测试一下: 
在Hierarchy视图中创建三个游戏对象,在Project视图中创建三条脚本,如下图所示,然后按照顺序将脚本绑定到对应的游戏对象上: 
Unity3D中脚本的执行顺序和编译顺序

三条脚本的代码完全一样,只是做了一点名称上的区分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using UnityEngine;
using System.Collections;
public class Scring0 : MonoBehaviour
{
    void Awake()
    {
        Debug.Log("Script0 ======= Awake");
    }
 
    bool isUpdate = false;
    void Update()
    {
        if(!isUpdate)
        {
            Debug.Log("Script0 ======= Update");
            isUpdate = true;
        }
    }
 
    bool isLateUpdate = false;
    void LateUpdate()
    {
        if(!isLateUpdate)
        {
            Debug.Log("Script0 ======= LateUpdate");
            isLateUpdate = true;
        }
    }
}

播放游戏,看看它们的执行顺序。如下图所示,Awake、Update、LateUpdate,无论运行游戏多少次,它们的执行顺序是完全一样的。
Unity3D中脚本的执行顺序和编译顺序


接着我们再做一个测试,把Script0的Update方法注释掉!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using UnityEngine;
using System.Collections;
public class Script0 : MonoBehaviour
{
  
    void Awake ()
    {
        Debug.Log("Script0 ========= Awake");
    }
  
//  bool isUpdate = false;
//  void Update ()
//  {
//      if(!isUpdate)
//      {
//          Debug.Log("Script0 ========= Update");
//          isUpdate = true;
//      }
//  }
  
    bool isLateUpdate = false;
    void LateUpdate()
    {
        if(!isLateUpdate)
        {
            Debug.Log("Script0 ========= LateUpdate");
            isLateUpdate = true;
        }
    }
}

再次运行游戏,看看它的结果。脚本的执行顺序和以前完全一样,Script0即便删除掉了Update方法,但是它也不会直接执行LateUpdate方法,而是等待Script1和Script2中的Update方法都执行完毕以后,再去执行所有的LateUpdate方法。 
Unity3D中脚本的执行顺序和编译顺序


通过这两个例子,我们就可以很清楚地断定,Unity后台是如何执行脚本的了。每个脚本的Awake、Start、Update、LateUpdate、FixedUpdate等等,所有的方法在后台都会被汇总到一起:

1
2
3
4
5
6
7
后台的Awake()
{
    // 这里暂时按照上图中的脚本执行顺序,后面会谈到其实可以自定义该顺序的
    脚本2中的Awake();
    脚本1中的Awake();
    脚本0中的Awake();
}

后台的方法Awake、Update、LateUpdate等等,都是按照顺序,等所有游戏对象上脚本中的Awake执行完毕之后,再去执行Start、Update、LateUpdate等方法的。

1
2
3
4
5
6
7
后台的Update()
{
    // 这里暂时按照上图中的脚本执行顺序,后面会谈到其实可以自定义该顺序的
    脚本2中的Update();
    脚本1中的Update();
    脚本0中的Update();
}

脚本的执行顺序

然后我们来看看这样一种情况:在脚本0的Awake方法中创建一个立方体对象,然后在脚本2的Awake方法中去获取这个立方体对象。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Script0.cs
using UnityEngine;
using System.Collections;
public class Script0 : MonoBehaviour
{
    void Awake ()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube);
    }
}
 
// Script2.cs
using UnityEngine;
using System.Collections;
public class Script2 : MonoBehaviour
{
    void Awake ()
    {
        GameObject go = GameObject.Find("Cube");
        Debug.Log(go.name);
    }
}

如果脚本的执行顺序是先执行Script0,然后再执行Script2,那么Script2中的Awake就可以正确地获取到该立方体对象;可是如果脚本的执行顺序是先执行Script2,然后是Script0,那么Script2肯定会报空指针错误的。

那么实际项目中的脚本会非常多,它们的先后执行顺序我们谁也不知道(有人说是按照栈结构来执行的,即后绑定到游戏对象上的脚本先执行。这一点可以从上面的例子中得到,但官方并没有这么说,还得进一步深入研究)。但一般的,建议在Awake方法中创建游戏对象或Resources.Load(Prefab)对象,然后在Start方法中去获取游戏对象或者组件,因为事件函数的执行顺序是固定的,这样就可以确保万无一失了。 
另外,Unity也提供了一个方法来设置脚本的执行顺序,在Edit -> Project Settings -> Script Execution Order菜单项中,可以在Inspector面板中看到如下图所示: 
Unity3D中脚本的执行顺序和编译顺序

点击右下角的"+"将弹出下拉窗口,包括游戏中的所有脚本。脚本添加完毕后,可以用鼠标拖动脚本来为脚本排序,脚本名后面的数字也越小,脚本越靠上,也就越先执行。其中的Default Time表示没有设置脚本的执行顺序的那些脚本的执行顺序。 
Unity3D中脚本的执行顺序和编译顺序

按照上面这张图的设置,我们再来看一下控制台的输出结果,来确认一下我们的设置是否起作用(注意:把Script0脚本中的Update方法取消注释): 
Unity3D中脚本的执行顺序和编译顺序


脚本的编译顺序

关于脚本的编译顺序很是头疼,官方的说法有点模糊,请看官方的解释: 
Unity3D中脚本的执行顺序和编译顺序

由于脚本的编译顺序会涉及到特殊文件夹,比如上面提到的Plugins、Editor还有Standard Assets等标准的资源文件夹,所以脚本的放置位置就非常重要了。下面用一个例子来说明不同文件夹中的脚本的编译顺序:

Unity3D中脚本的执行顺序和编译顺序

实际上,如果你细心的话会发现,如果在你的项目中建立如上图所示的文件夹层次结构时,编译项目之后会在项目文件夹中生成一些文件名中包含Editor、firstpass这些字样的项目文件。比如按照上图的文件夹结构,我们打开项目文件夹来看一下产生的项目文件是什么样的? 
Unity3D中脚本的执行顺序和编译顺序

下面就来详细探讨一下这些个字样是什么意思?它们与脚本的编译顺序有着怎样的联系?


1、首先从脚本语言类型来看,Unity3d支持3种脚本语言,都会被编译成CLI的DLL

如果项目中包含有C#脚本,那么Unity3d会产生以Assembly-CSharp为前缀的工程,名字中包含”vs”的是产生给Vistual Studio使用的,不包含”vs”的是产生给MonoDevelop使用的。

项目中的脚本语言 工程前缀 工程后缀
C# Assembly-CSharp csproj
UnityScript Assembly-UnityScript unityproj
Boo Assembly-Boo booproj

如果项目中这三种脚本都存在,那么Unity将会生成3种前缀类型的工程。

2、对于每一种脚本语言,根据脚本放置的位置(其实也部分根据脚本的作用,比如编辑器扩展脚本,就必须放在Editor文件夹下),Unity会生成4中后缀的工程。其中的firstpass表示先编译,Editor表示放在Editor文件夹下的脚本。

在上面的示例中,我们得到了两套项目工程文件:分别被Virtual Studio和MonoDevelop使用(后缀包不包含vs),为简单起见,我们只分析vs项目。得到的文件列表如下: 
Assembly-CSharp-filepass-vs.csproj 
Assembly-CSharp-Editor-filepass-vs.csproj 
Assembly-CSharp-vs.csproj 
Assembly-CSharp-Editor-vs.csproj

根据官方的解释,它们的编译顺序如下: 
(1)所有在Standard Assets、Pro Standard Assets或者Plugins文件夹中的脚本会产生一个Assembly-CSharp-filepass-vs.csproj文件,并且先编译; 
(2)所有在Standard Assets/Editor、Pro Standard Assets/Editor或者Plugins/Editor文件夹中的脚本产生Assembly-CSharp-Editor-filepass-vs.csproj工程文件,接着编译; 
(3)所有在Assets/Editor外面的,并且不在(1),(2)中的脚本文件(一般这些脚本就是我们自己写的非编辑器扩展脚本)会产生Assembly-CSharp-vs.csproj工程文件,被编译; 
(4)所有在Assets/Editor中的脚本产生一个Assembly-CSharp-Editor-vs.csproj工程文件,被编译。

之所以按照这样建立工程并按此顺序编译,也是因为DLL间存在的依赖关系所决定的。

好了,到此为止,我们可以很容易地判断出上面举的实例中,脚本的编译顺序(实际上,我已经把顺序写在了脚本的文件名中了)

Unity3D中脚本的执行顺序和编译顺序的更多相关文章

  1. 【转】Unity3D中脚本的执行顺序和编译顺序

    支持原文,原文请戳: Unity3D中脚本的执行顺序和编译顺序 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与脚本有关的也就是编译和执行啦 ...

  2. 【转】Unity3D中脚本的执行顺序和编译顺序(vs工程引用关系)

    http://www.cnblogs.com/champ/p/execorder.html 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与 ...

  3. (转)Unity3D中脚本的执行顺序和编译顺序(vs工程引用关系)

    自:http://www.cnblogs.com/champ/p/execorder.html 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行 ...

  4. php脚本的执行过程(编译与执行相分离)

    php脚本的执行过程(编译与执行相分离) 深入理解PHP代码的执行的过程 PHP程序的执行流程 Apache + PHP 的并发访问

  5. Unity基础-脚本的加载与编译顺序

    脚本的加载与编译顺序 C#是以Assembly(汇编集)为一个基本单元组织代码的,dll就是一个assembly,dll之间有加载以来顺序 Assets/*.dll Stamdard Assets/* ...

  6. Unity中脚本的执行顺序总结(@WhiteTaken)

    (Editor)以上是Unity官方文档中的截图,脚本在被挂载到物体上,会启用Editor的方法Reset. (Initialization)当执行脚本开始,初始化的过程中,依次执行的是Awake-& ...

  7. Postman集合/文件夹/请求中脚本的执行顺序

    Postman的Collection(集合)/Folder(集合的子文件夹)/Request(请求)中都有Pre-request Script(请求前脚本)和Tests(请求后脚本) 这个功能类似于不 ...

  8. Unity3D中关于场景销毁时事件调用顺序的一点记录

    先说一下我遇到的问题,我弄了一个对象池管理多个对象,对象池绑定在一个GameObject上,每个对象在OnBecameInvisible时会进行回收(即移出屏幕就回收),但是当场景切换或停止运行程序时 ...

  9. (转)unity3d中脚本生命周期(MonoBehaviour lifecycle)

    自:http://blog.csdn.net/qitian67/article/details/18516503 最近在做一个小示例,发现类继承于MonoBehaviour的类,有很多个方法,于是乎必 ...

随机推荐

  1. .jshintrc配置

    在root目录创建一个.jshintrc文件插入下面的代码即可.也可以自行配置. { // // 强制选项 // // When set to true, these options will mak ...

  2. JS魔法堂: Native Promise Only源码剖析

    一, 前言 深入学习Promise的朋友应该都看过<深入理解Promise五部曲>这一系列的文章, 以解除回调地狱之外的观点来剖析Promise更多的内涵,确实十分精彩. Part 1: ...

  3. 用sessionStorage实现页面之间的数据传输

    1.sessionStorage主要含几种方法: //页面A:存放一个简单的字符串 sessionStorage.obj = '123'; //页面B:取到给obj var str = session ...

  4. ubuntu下Tomcat7的安装和配置

    和前几个软件一样,Tomcat 同样是由JAVA开发的,所以,在安装前一定要装好JDK. 大家可以到 http://tomcat.apache.org/download-70.cgi 下载最新的Tom ...

  5. python之递归

    递归的定义:即对自己自身内容的引用. 有用的递归函数应包含以下几步份: 当函数直接返回值时有基本的实例(最小可能性问题): 递归实例,包括一个或者多个问题较小部分的递归调用: 递归的关键就是将问题分解 ...

  6. (四)左右ng-app自己主动bootstrap相框

    博客之前 (三)ng-app的使用困惑和angularJS框架的自己主动载入 提出了使用ng-app指令的情况.之前认为出现第4和第5种情况非常奇怪,由于仅仅看到了现象,没有看到本质.JS错误.最直观 ...

  7. hdu 2896 病毒侵袭 AC自动机&lpar;查找包含哪些子串&rpar;

    病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  8. ztree 文件夹类型的 树状图

    未套程序的源代码: 链接:http://pan.baidu.com/s/1nuHbxhf 密码:4aw2 已套程序的源代码: css样式: /*发布邮件 选择领导弹窗*/ .xuandao{ disp ...

  9. 一文学会用 Tensorflow 搭建神经网络

    http://www.jianshu.com/p/e112012a4b2d 本文是学习这个视频课程系列的笔记,课程链接是 youtube 上的,讲的很好,浅显易懂,入门首选, 而且在github有代码 ...

  10. Spring Boot 2&period;0 设置网站默认首页

    Spring Boot设置默认首页,方法实验OK如下 附上Application启动代码 /** * @ClassName Application * @Description Spring-Boot ...