Awake/Start/OnEnable 辨析

unity开发 专栏收录该内容
13 篇文章 1 订阅

前言

Start() 作为Unity创建文件时的模板里自带函数,应该大多数编写过脚本的人都知道。一般来说,Start() 可以用来做变量初始化等只在游戏开始时运行一次的代码。相比之下,知道Awake() 的人可能就要少了一半,它和Start类似,在脚本的生命周期中只会被执行一次,并且比Start要先执行。其实还有另外一个和它们类似的函数叫做OnEnable,当我们在写一些用完一次就够了的代码时,应当立刻想到它们仨,但是具体选择哪一个,其实还是有一点区别的。

概念分析

按照惯例,我们结合官方文档逐个分析:
1. Awake

Awake: This function is always called before any Start functions and also just after a prefab is instantiated. (If a GameObject is inactive during start up Awake is not called until it is made active.)

我的个人理解是这个函数总是在任何Start之前被调用,并且可以在预制体被实例化后立即调用。括号里面的一句话也很关键,那就是如果一个GameObject处于未激活状态,那么Awake直到该物体被激活后才会被调用。
这里有一个单词需要被特别注意,那就是active/inactive。很多人会把它和enable/disable搞混,其实两者适用的对象完全不同。Active是相对于GameObject来说的,如果你查阅GameObject的API,你会发现GameObject具有一个SetActive函数专门用来控制游戏物体的激活状态。相比之下,Enable则是相对于一个组件来说的。一个游戏物体可能附加有许多个不同的组件,包括内置组件(Transform,RigidBody等)以及由用户编写的Script(通常继承自MonoBehaviour)。其实这两种组件都是继承自Component类的。因此设置Enable属性比Active影响的范围更小,从语义上理解,Disable顶多就是把人打残(adj.残废的),Inactive直接把人打没气了,动不了了(adj. 不活跃的),当你分不清时这么一想基本也就了然了。

进阶知识: 若父类处于Inactive状态,GameObject.SetActive并不能将GameObject激活,而是改变了局部状态selfActive。当它的父类被激活时,GameObject才会根据这个变量确定是否激活。

当脚本的enable状态为false时,该脚本中Update函数不会再被调用。

2. OnEnable

OnEnable: (only called if the Object is active): This function is called just after the object is enabled. This happens when a MonoBehaviour instance is created, such as when a level is loaded or a GameObject with the script component is instantiated.

OnEnable与Awake类似,只有在Object处于激活状态时才会被调用。但是它们的触发时机则是大大的不同。Awake只要求脚本挂载的GameObject处于激活状态就会被执行,即便是Awake所在的脚本没有被启用(enable=true)也无所谓。而OnEnable除了满足GameObject被激活外,还实时的检测函数所在的脚本是否被启用,这也保证了OnEnable在每次GameObject.Active或者Behaviour.enable的状态变化时都会被调用

3.Start

Start: Start is called before the first frame update only if the script instance is enabled.

这么一看,Start比起Awake,和OnEnable的相似度反而更高点。因为它也在乎脚本是否被启用。除此之外,Start只会在Update第一次更新前被调用,这也就是说,即便你频繁地 enable/disable 某个脚本,Start也只会被调用一次,然而OnEnable却可以每次都被调用。

代码演示

为了验证我们之前的猜想,可以通过编写一个很简单的脚本来进行测试。

public class GameLogicA : MonoBehaviour {
    public string role = "A";

    private void Awake()
    {
        print(role + " is Awake");
    }
    // Use this for initialization
    void Start () {
        print(role + " is Start");
    }

    private void OnEnable()
    {
        print(role + " is OnEnable");
    }
}

把该脚本绑在一个GameObject上,然后就可以在Inspector面板上修改该物体的Active信息和附加在该物体上的组件的Enable信息。像我一样把脚本的enable信息修改为disable(把checkbox取消勾选),然后运行。

修改物体状态

因为我们在游戏开始时没有启用GameLogicA脚本,所以OnEnable和Start函数均不会被调用,但是Awake仅考虑GameObject的状态,所以依然可以正常执行。接下来,我们修改脚本状态为enable=true。你可以看到Start和OnEnable均被调用,并且OnEnable优先于Start执行。重复修改脚本状态,你会发现Start不会再被调用,而每次OnEnable均可以正常调用。

总结

Awake/Start/OnEnable 三者调用的时机都各有差异,所以我们在使用时也应当综合考虑。一般来说,我会把场景加载成功后一定会执行的操作放在Awake中,例如设置屏幕的熄灭时间。在Update函数中用到的组件可以考虑在Start中进行初始化,因为Start一定会在Update第一次调用之前执行(并且仅执行一次);但是若组件在除Update以外的函数也被用到,则将其放在Awake中进行初始化往往更安全。除此之外,OnEnable可以考虑应用于需要在脚本启用时频繁调用的操作,例如动态生成面板,并且可以和OnDestroy配合使用。

  • 15
    点赞
  • 0
    评论
  • 25
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值