Unity3D脚本中Start和Awake的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D脚本中Start和Awake的区别相关的知识,希望对你有一定的参考价值。

参考技术A Unity3D初学者经常把Awake和Start混淆。
简单说明一下,Awake在MonoBehavior创建后就立刻调用,Start将在MonoBehavior创建后在该帧Update之前,在该Monobehavior.enabled == true的情况下执行。
void Awake ()

//初始化函数,在游戏开始时系统自动调用。一般用来创建变量之类的东西。

void Start()

//初始化函数,在所有Awake函数运行完之后(一般是这样,但不一定),在所有Update函数前系统自动条用。一般用来给变量赋值。
我们通常书写的脚本,并不会定义[ExecuteInEditMode]这个Attribute,所以Awake和Start都只有在Runtime中才会执行。
例1:

public class Test : MonoBehaviour
void Awake ()
Debug.Log("Awake");
enabled = false;


void Start ()
Debug.Log("Start");


以上代码,在Awake中我们调用了enabled = false; 禁止了这个MonoBehavior的update。由于Start, Update, PostUpdate等属于runtime行为的一部分,这段代码将使Start不会被调用到。
在游戏过程中,若有另外一组代码有如下调用:
Test test = go.GetComponent<Test>();
test.enabled = true;
这个时候,若该MonoBehavior之前并没有触发过Start函数,将会在这段代码执行后触发。
例2:
player.cs
private Transform handAnchor = null;
void Awake () handAnchor = transform.Find("hand_anchor");
// void Start () handAnchor = transform.Find("hand_anchor");
void GetWeapon ( GameObject go )
if ( handAnchor == null )
Debug.LogError("handAnchor is null");
return;

go.transform.parent = handAnchor;

other.cs
...
GameObject go = new GameObject("player");
player pl = go.AddComponent<player>(); // Awake invoke right after this!
pl.GetWeapon(weaponGO);
...
以上代码中,我们在player Awake的时候去为handAnchor赋值。如果我们将这步操作放在Start里,那么在other.cs中,当执行GetWeapon的时候就会出现handAnchor是null reference.
总结:我们尽量将其他Object的reference设置等事情放在Awake处理。然后将这些reference的Object的赋值设置放在Start()中来完成。
当MonoBehavior有定义[ExecuteInEditMode]时
当我们为MonoBehavior定义了[ExecuteInEditMode]后,我们还需要关心Awake和Start在编辑器中的执行状况。
当该MonoBehavior在编辑器中被赋于给GameObject的时候,Awake, Start 将被执行。
当Play按钮被按下游戏开始以后,Awake, Start 将被执行。
当Play按钮停止后,Awake, Start将再次被执行。
当在编辑器中打开包含有该MonoBehavior的场景的时候,Awake, Start将被执行。
值得注意的是,不要用这种方式来设定一些临时变量的存储(private, protected)。因为一旦我们触发Unity3D的代码编译,这些变量所存储的内容将被清为默认值。
参考技术B 不会,系统会马上提示错误unity本身就不建议new出monobehaviour,如果你非要new,就让那个类不要继承monobehaviour

[Unity] Awake 与 Start 的使用, 为什么将 GetComponent 逻辑放到 Awake 中

如果你的 Unity 脚本是这么写的, 那么你需要注意了

using UnityEngine;

public class MyScript : MonoBehaviour

    SpriteRenderer spriteRenderer;
    void Start()
    
        spriteRenderer = GetComponent<SpriteRenderer>();
    

Unity 管线

Unity 不是完全渲染完 A 然后再去渲染 B 的, 而是先渲染 A 的一部分, 然后渲染 B 的一部分, 再渲染其他的一部分.

然后再次渲染 A 的一部分, 渲染 B 的一部分, 渲染其他的一部分

换做脚本, 假设有 ABC 三个对象, 执行的顺序应该是.

A.Method1
B.Method1
C.Method1

A.Method2
B.Method2
C.Method2

A.Method3
B.Method3
C.Method3

Awake 和 Start

Awake 和 Start 就相当于上面的 Method1, Method2, 所有对象的 Awake 都被调用一遍后, 然后再调用所有对象的 Start

不同对象顺序

Unity 是单线程, 所以 ABC 的 Method1 这种, 也是依次调用的, 也就是说, A 在调用 Method1 的时候, B 的 Method1 还没有被调用, 只能 A 的执行完之后轮到 B, 然后轮到 C

未初始化的变量

如果你把一些变量的初始化放在 Start 中, 那么在调用这个对象的 Start 前, 它的这些变量, 是完全没有初始化的, 为 null

引用未初始化的变量

如果你在某个对象没有调用 Start 前, 也就是它的变量未初始化时, 就调用这个对象的脚本暴露出来的一些公开方法, 例如依赖于 spriteRenderer 的 “角色姿态变换”, 那么, 由于 spriteRenderer 没有初始化, 你的程序就引发 NullReferenceException 了

假设 A 的 Start 中需要调用 B 的 “MethodQwq”, 在 B 的 MethodQwq 中, 用到了 B 类的字段 “spriteRenderer”, 但是, spriteRenderer 是在 B 的 Start 方法中被赋值的

当 A Start 执行时, 此时 B 的 Start 还没执行, B 的 spriteRenderer 为 null

A 的 Start 调用 B 的 MethodQwq, 因为 spriteRenderer 为空, 所以引发了 NullReferenceException

解决方案

统一在 Awake 方法中做变量的初始化, 统一在 Start 中做逻辑的开始执行. 这样在 Start 调用时, 所有对象的 Awake 已经调用过, 变量也全部初始化, 所以不会引发上述问题

以上是关于Unity3D脚本中Start和Awake的区别的主要内容,如果未能解决你的问题,请参考以下文章

只怕没有几个人能说清楚系列之一:Awake/Start区别

Unity中Awake和Start的区别

awake()和start()还有update(),fixedupdate()的差别

Unity不同脚本或者多个脚本之间的执行顺序Awake, Start

Unity脚本的生命周期 同一脚本/不同脚本/游戏对象激没激活/脚本激没激活,几种情况下的Awake,OnEnable,Start的执行顺序

Unity - 脚本的 Awake() 是不是在其他脚本的 Start() 之前运行?