Unity中Awake和Start的区别

正式开始学习Unity了。当然,第一个遇到的问题就是Awake和Start的问题,之前在网上查过一下这两者的区别,简单记忆了一下,认为自己知道了两者的区别。不过实际用起来,发现对于这两者到底是什么区别,心里还是没底,而且最关键的是木有Unityt的源代码,所以我们只能是通过文档或者是别人的blog来了解,当然,还有一个办法就是自己做一下实验,实践是检验真理的唯一标准。

一.官方解释

先来看看Unity官方对于这两个函数的解释:

Awake is called when the script instance is being loaded.

Awake is used to initialize any variables or game state before the game starts. Awake is called only once during the lifetime of the script

instance. Awake is called after all objects are initialized so you can safely speak to other objects or query them using

eg. GameObject.FindWithTag. Each GameObject‘s Awake is called in a random order between objects. Because of this, you should use

Awake to set up references between scripts, and use Start to pass any information back and forth. Awake is always called before any

Start functions. This allows you to order initialization of scripts. Awake can not act as a coroutine.

Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.

Like the Awake function, Start is called exactly once in the lifetime of the script. However, Awake is called when the script object is

initialised, regardless of whether or not the script is enabled. Start may not be called on the same frame as Awake if the script is not

enabled at initialisation time.

The Awake function is called on all objects in the scene before any object‘s Start function is called. This fact is useful in cases

where object A‘s initialisation code needs to rely on object B‘s already being initialised; B‘s initialisation should be done in Awake

while A‘s should be done in Start.Where objects are instantiated during gameplay, their Awake function will naturally be called after

the Start functions of scene objects have already completed.

解释一下:

Awake在脚本被实例化的时候就会被调用(不管脚本是不是enable的),而且在脚本的生命周期中只会被调用一次。Awake是在所有对象实例化之后,所以我们可以放心大胆地去使用诸如GmeObject.Fine之类的方法来在Awake中给各个组件之间添加引用 关系。Awake会在所有对象的Start之前调用,但是注意不同对象之间的Awake顺序是不得而知的。

Start是在对象被第一次enable之后,在Update之前调用的,Start在脚本的生命周期中也只可能被调用一次。Start可能不会被立刻调用,比如我们之前没有让其enable,当脚本被enable时,Start才会被调用。

官方文档的建议是:尽量在Awake函数中进行初始化操作,除非有A依赖B,B必须在A实例化之前完成初始化,那么A在Start,B放在Awake中可以保证A在B之后才被初始化(不过个人感觉还是应该尽量都在Awake中进行对象间的引用,然后手动调用Init函数进行初始化,这样可以自己控制初始化的顺序)。

二.几个实验

1.关于Awake,Start,Update的执行时机,以及不被enable时的情况

在场景中创建一个空的Object,把下面的脚本挂上去:

     public class StartAwakeTest : MonoBehaviour {

	// Use this for initialization
	void Start () {
        Debug.Log("Start is called!");
	}

    void Awake()
    {
        Debug.Log("Awake is called!");
    }

	// Update is called once per frame
	void Update () {
        Debug.Log("Update is called!");
	}
}

直接运行游戏时,输出如下:

和官方文档所说的一致,这个我们早就知道了。不过,如果我们一开始让脚本对象不被激活,最简单的方法就是在编辑器的Inspector面板上,找到对应的脚本前面有一个小的勾选框,默认是被勾选的,就是被激活的,如果取消勾选,那么这个脚本组件就不会被激活。

我们试一下,取消脚本的激活,然后运行:

我们看到,Start和Update函数都没有被执行,而Awake函数仍然被执行了。可见,不管Object被不被激活,Awake函数都会被执行。这时,我们手动勾选一下Start Awake Test前面的勾选框,结果就和第一幅图一样啦,Start和Update都开始被执行了。

2.通过脚本创建的对象的Awake和Start运行情况

我们把之前的脚本去掉,然后在对象上挂上这样一个脚本:

using UnityEngine;
using System.Collections;

public class CreateObj : MonoBehaviour {

    //此处通过一个引用来保存对象,因为被取消激活的对象是不能被find函数找到的!!!
    private GameObject go = null;

    void Awake()
    {
        go = new GameObject("game object");
    }

    void Update()
    {
        //添加脚本组件,默认不激活
        if (Input.GetKeyUp(KeyCode.F1))
        {

            go.AddComponent<StartAwakeTest>();
            //只让StartAwakeTest Component 不激活 = 在编辑器里面取消脚本前面的勾选
            //go.GetComponent<StartAwakeTest>().enabled = false;
            //直接让Obj不激活
            go.SetActive(false);
        }

        //将其激活
        if (Input.GetKeyUp(KeyCode.F2))
        {

            if (go == null)
                return;
            go.SetActive(true);
        }

        //将其取消激活
        if (Input.GetKeyUp(KeyCode.F3))
        {

            if (go == null)
                return;
            go.SetActive(false);
        }
    }
}

运行之后,对象虽然创建了,但是没有挂上脚本,我们通过F1按钮,控制其动态添加脚本,这时,会输出Awake,但是由于我们设置了对象是非Active的,所以Start函数并没有调用:

当我们按下F2时,该object被激活,这时Start函数和Update函数会开始执行。输出“Start is called!","Update is called"当我们按下F3之后,对象被取消激活,这时,Update不会再被执行。

3.对象第二次被激活时Start会被再次调用吗

当我们再次按下F2,让对象第二次被激活,Start函数还会调用吗?(这也是我最关心的)

可见,虽然Object被第二次激活,但是Start函数不会再被调用了!说明Start函数只有在第一次被激活的时候才会被调用!!!

三.对象初始化的时机

Awake和Start两者都只能在生命周期中被调用一次,而且都是最先调用的,所以研究Awake和Start就是为了研究Unity对象的初始化机制,我们进一步地看一下Uniyt初始化时的流程。

1.Find方法

先来看一个函数,Find。我们知道Unity的Find函数可以根据名字查询到场景中的物体,但是这个物体必须是被激活的,如果我们把这个物体SetActive(false)了,那么这个函数是找不到对应物体的。比如下面的一段代码:

void Awake()
    {
        GameObject go = new GameObject("game object");
        go.SetActive(false);

        GameObject go1 = GameObject.Find("game object");
        if (go1 == null)
            Debug.Log("Can't find!");
        else
            Debug.Log("Find!");
    }

结果:

为什么要看Find方法呢,因为很多情况下,我们都是通过Find来找到对象之间的对应关系。我们通过Find可以进行下一步的实验:

2.Awake的调用时机

我之前有点搞不懂Awake的调用时机,担心会出现在一个Obj被Awake了,其他的没有被Awake,会造成对象空引用的错误,但是事实上并不是这样,看这样一个例子:

以下两段脚本分别被绑定在两个对象上:

Obj1对象上的脚本:

using UnityEngine;
using System.Collections;

public class Component1 : MonoBehaviour {

    void Awake()
    {
        GameObject go = GameObject.Find("Obj2");
        if (go != null)
            Debug.Log("Obj2 is found!");
        go.GetComponent<Component2>().Test();
    }

    public void Test()
    {
        Debug.Log("Test in Component1 is called");
    }

}

Obj2对象上的脚本:

using UnityEngine;
using System.Collections;

public class Component2 : MonoBehaviour {

    void Awake()
    {
        GameObject go = GameObject.Find("Obj1");
        if (go == null)
            Debug.Log("Obj1 is  not found!");
        Debug.Log("Obj1 is found!");
        go.GetComponent<Component1>().Test();
    }

    public void Test()
    {
        Debug.Log("Test in Component2 is called");
    }
}

运行结果如下:

可见,在两个对象的Awake函数中,都通过名字查找到了对方的对象,并且调用了对方的函数。这说明在Awake函数调用之前,所有的对象已经创建完毕了!所以我们可以通过这种方式来在Awake函数中放心大胆的设置对象之间的引用关系。

3.Start,Awake,和自定义函数的调用顺序

还有一个疑问,就是如果我们自己通过脚本建立一个对象,然后马上调用其中的一个函数,那么,Start和Awake还会在之前被调用吗?不多说,上代码。

在场景中建立一个对象,挂上下面的脚本:

using UnityEngine;
using System.Collections;

public class Component1 : MonoBehaviour {

    void Awake()
    {
        Debug.Log("Awake in Original GameObject is called!");
    }

    void Start()
    {
        Debug.Log("Start in Original GameObject is called!");
        GameObject go = new GameObject("new Obj");
        go.AddComponent<Component2>();
        go.GetComponent<Component2>().Test();
    }

}

然后准备另一个脚本,供动态生成的对象使用:

using UnityEngine;
using System.Collections;

public class Component2 : MonoBehaviour {

    void Awake()
    {
        Debug.Log("Awake in new GameObject is called!");
    }

    void Start()
    {
        Debug.Log("Start in new GameObject is called!");
    }

    public void Test()
    {
        Debug.Log("Test in new GameObject is called");
    }
}

结果如图:

Look!Awake函数最先被调用了,然后接着是我们自定义的Test函数,最后才是Start函数!!!这里应该是很容易出现问题的地方,比如Test函数中要用到一些值,而这些值应该被初始化,如果我们把初始化放在了Start函数中,那么此处这些值还没有被初始化,那么就会出现空引用异常等错误。我之前也是遇到了很多次,查了半天发现都是把对象的初始化放在了Start函数中,结果浪费了大量的时间,这也是我写这篇文章的重要原因之一,希望大家少走弯路!

四.总结

最后总结一下Awake和Start的异同点:

相同点:

1)两者都是对象初始化时调用的,都在Update之前,场景中的对象都生成后才会调用Awake,Awake调用完才会调用Start,所有Start调用完才会开始Update。

2)两者在对象生命周期内都只会被调用一次,即初始化时被调用,之后即使是在被重新激活之后也不会再次被调用。

不同点:

1)Awake函数在对象初始化之后立刻就会调用,换句话说,对象初始化之后第一调用的函数就是Awake;而Start是在对象初始化后,第一次Update之前调用的,

在Start中进行初始化不是很安全,因为它可能被其他自定义的函数抢先。

2)Awake不管对象是否是Active,脚本是否enabled都会被调用,可以说是无论如何都会被调用的;而Start如果对象被SetAcive(false)或者enabled= false了是

不会被调用的。

时间: 2024-10-14 13:32:59

Unity中Awake和Start的区别的相关文章

unity中 Object 与 object的区别.

小写的object是泛类, 引用的是System.Object. 大写的Object引用的是UnityEngine.Object. UnityEngine.Object继承System.Object. 需要注意的是重载的运算符. System.Object a = new System.Object(); UnityEngine.Object b = new UnityEngine.Object(); Debug.Log (a); //System.Object Debug.Log (b); /

Unity中Awake与Start函数的调用情况总结(转)

在Unity中编写脚本时,有一系列的可重写(override)函数供我们使用,其中的Awake与Start两个函数作为初始化与设置之用,几乎在每个脚本中都要用到.因此,正确的把握这两个函数的调用时机,就能让我们在程序开发过程中避免一些错误,提高开发效率.比较懒,所以就没有上图,欢迎大家的批评指正: ) 1.  Awake函数 首先,我们来看unity的参考手册中对Awake函数的一些说明情况: 当脚本实例被加载时会调用Awake函数:Awake函数在所有的游戏对象被初始化完毕之后才会被调用:在脚

Unity中Awake与Start函数361天恒平台搭建的调用情况总结

在Unity中编写361天恒平台搭建论坛:haozbbs.com Q1446595067 脚本时,有一系列的可重写(override)函数供我们使用,其中的Awake与Start两个函数作为初始化与设置之用,几乎在每个脚本中都要用到.因此,正确的把握这两个函数的调用时机,就能让我们在程序开发过程中避免一些错误,提高开发效率. 1. Awake函数 首先,我们来看unity的参考手册中对Awake函数的一些说明情况: 当脚本实例被加载时会调用Awake函数:Awake函数在所有的游戏对象被初始化完

Unity中LoadLevel与LoadLevelAsync的区别

1.LoadLevel 同步加载 写法:Application.LoadLevel(“name”); 优点:读取场景使用同步的方法就可以,因为是同步方法所以读取的速度是最快的,也不用更新界面,因为同步执行方法的时候程序在等待读取结束. 缺点:Loading的时候如果界面不动,那么用户体验肯定不佳,因为读取的时间如果过长用户就会误以为手机卡死了. 2.LoadLevelAsync 异步加载 写法:Application.LoadLevelAsync (“name”); 优点:读取的时候界面可以有动

2019.9.27 Unity中Sprite和UI Image的区别

来源:https://blog.csdn.net/coffeecato/article/details/78536488 coffeecato写在前面:本文确实不错,作者用以说明自动生成网格的示图非常具有代表性,从drawcall的生成过程分析性能开销的重点,引出了overdraw和达到GPU像素填充率限制的原因,从中也可以看出作者对这个主题的理解颇有深度.查看作者的个人自述,居然是个2012年毕业的小伙子,后生可畏啊!翻译本文对自己也是个考验.英文水平捉急,如果错误请多多指正. 原文:UNIT

矩阵与点乘与叉乘在Unity中的区别

今天豁然开朗来记录一下我理解的矩阵以及四元数和点乘与叉乘的理解. 首先说矩阵以及四元数 矩阵的乘法是根据n*m列来获取的,n的行数是代表结果的行数,m的列数代表结果的列数. 红色与黑色表示第一个矩阵中的4个数,紫色和灰色表示第二个矩阵的4个数,矩阵的乘法是第一个矩阵的n行与第二个矩阵的m列想乘并把乘机相加的到结果矩阵 第(n,m)位置的结果. 而四元数表示一个[x,y,z,1] * 4*4的矩阵.每组的矩阵正好只有一个有值.[1 ,0,0,0] [0,1,0,0][0,0,1,0][Tx,Ty,

Unity中的Object和object的区别

Object是UnityEngine.Object,是Unity所有组件Component和GameObject的父类: object在Unity中是System.Object 举个简单的例子: Debug.Log(gameObject is Object); Debug.Log(gameObject is object); 前者返回true,后者也返回true: int num = 5; Debug.Log(num is Object); Debug.Log(num is object); 前

写给VR手游开发小白的教程:(四)补充篇,详细介绍Unity中相机的投影矩阵

这篇作为上一篇的补充介绍,主要讲Unity里面的投影矩阵的问题: 上篇的链接写给VR手游开发小白的教程:(三)UnityVR插件CardboardSDKForUnity解析(二) 关于Unity中的Camera,圣典里面对每一项属性都做了简要的介绍,没看过的小伙伴传送门在下面 http://www.ceeger.com/Components/class-Camera.html 一.裁剪面 先从这个专业的词汇开始,以下是圣典对裁剪面的介绍: The Near and Far Clip Plane

在Unity中使用事件/委托机制(event/delegate)进行GameObject之

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,[狗刨学习网]unity极致学院,致力于打造业内unity3d培训.学习第一品牌. 一对多的观察者模式机制有什么缺点? 如果你对如何在Unity中使用事件/委托机制还不太了解,建议您查看我的前一篇文章:[Unity3D技巧]在Unity中使用事件/委托机制(event/delegate)进行GameObject之间的通信 在前一篇博客里面,我们写到