unity中awak,start,update之间的关系(转)

news/2024/7/3 3:43:41

正式开始学习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,把下面的脚本挂上去:
[csharp] view plain copy
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运行情况

我们把之前的脚本去掉,然后在对象上挂上这样一个脚本:
[csharp] view plain copy
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);  
}  

}
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
31
32
33
34
35
36
37
38
39
40
}
运行之后,对象虽然创建了,但是没有挂上脚本,我们通过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)了,那么这个函数是找不到对应物体的。比如下面的一段代码:
[csharp] view plain copy
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!");  

}
1
2
3
4
5
6
结果:

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

2.Awake的调用时机

我之前有点搞不懂Awake的调用时机,担心会出现在一个Obj被Awake了,其他的没有被Awake,会造成对象空引用的错误,但是事实上并不是这样,看这样一个例子:
以下两段脚本分别被绑定在两个对象上:
Obj1对象上的脚本:
[csharp] view plain copy
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().Test();
}

public void Test()
{
Debug.Log(“Test in Component1 is called”);
}
1
2
3
4
5
6
7
8
9
10
11
12
}

Obj2对象上的脚本:
[csharp] view plain copy
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().Test();
}

public void Test()
{
Debug.Log(“Test in Component2 is called”);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
}
运行结果如下:

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

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

还有一个疑问,就是如果我们自己通过脚本建立一个对象,然后马上调用其中的一个函数,那么,Start和Awake还会在之前被调用吗?不多说,上代码。
在场景中建立一个对象,挂上下面的脚本:
[csharp] view plain copy
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();
go.GetComponent().Test();
}
1
2
3
4
5
6
7
8
9
10
11
12
}
然后准备另一个脚本,供动态生成的对象使用:
[csharp] view plain copy
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”);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
}
结果如图:

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了是
不会被调用的。


http://www.niftyadmin.cn/n/4354902.html

相关文章

Basic FIFO Queue

Queue - 一种线程安全的FIFO实现 Python的Queue模块提供一种适用于多线程编程的FIFO实现。它可用于在生产者(producer)和消费者(consumer)之间线程安全(thread-safe)地传递消息或其它数据&#xff0c;因此多个线程可以共用同一个Queue实例。Queue的大小&#xff08;元素的个数&…

类 对象的定义实例和引用

类的定义、对象的定义、对象实例化、对象引用}类&#xff1b;可以理解为一类对象高度抽象而成的集合体&#xff0c;表示他们共性的组件 对象&#xff0c;则是通过类来创建一个对象&#xff1b;可以创建N多个对象 类名 对象名——则创建一个对象&#xff1b;此时称之为定义一个…

一个屌丝程序员的青春(三六三)

10后&#xff1a;什么是工作&#xff1f; 00后&#xff1a;为什么要工作&#xff1f; 95后&#xff1a;感觉不爽就离职。 90后&#xff1a;领导骂我就离职。 80后&#xff1a;有收入高就离职。 70后&#xff1a;为什么要离职&#xff1f; 60后&#xff1a;什么是离职&…

一个屌丝程序员的青春(三六四)

很多人越来越不愿意发朋友圈了&#xff0c;可能是以下原因&#xff1a; 一、生活压力大、工作压力大 越来越多的人可能每天只点开一次朋友圈&#xff0c;更别说去发朋友圈了。那些孤独的、工作轻松的、空闲时间多的人&#xff0c;可能每天会刷很多遍手机&#xff0c;翻看很多…

学习:

transform、 time、 rigid body、 input(get key、geotaxis、getaixsraw、mouse position、getbuttondown) vector3、 获取鼠标坐标、 屏幕坐标和世界坐标、 gameobject、 数组、 结构体、

一个屌丝程序员的青春(三六五)

美剧、英剧里高智商的刑侦推理剧&#xff1a; 一、《基本演绎法》 讲述福尔摩斯的故事&#xff0c;从伦敦搬到纽约。有毒瘾的福尔摩斯和有酒瘾的女版华生会擦出怎样的火花呢&#xff1f;相对于英剧版本多了些幽默戏份&#xff0c;但是沉重的案件依然是本剧的主打看点。 二、…

互联网工作原理

计算机网络是由许多计算机组成的&#xff0c;要实现网络的计算机之间传输数据&#xff0c;必须要   作两件事&#xff0c;数据传输目的地址和保证数据迅速可靠传输的措施&#xff0c;这是因为数据在传输   过程中很容易丢失或传错&#xff0c;Internet使用一种专门的计算机…

一个屌丝程序员的青春(三六六)

印度电影《起跑线》讲的就是一对夫妇为了让孩子从小就获得最好的教育、走上人生巅峰而绞尽脑汁择校的故事。 《围炉夜话》有一言&#xff0c;“富家惯习骄奢&#xff0c;最难教子&#xff1b;寒士欲谋生活&#xff0c;还是读书”。可是&#xff0c;当“寒门再难出贵子”成为社…