微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

《学Unity的猫》——第四章:认识Unity脚本组件的生命周期

简介:我是一名Unity游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity游戏开发。
于是,发生了一系列有趣的故事。

在这里插入图片描述

4.1 怎么没有老鼠组件

我:“皮皮,你在干嘛?”
皮皮:“我在给物体挂组件,可是我找了半天,也没找到老鼠组件。”
我:“哈哈哈,Unity没有帮我们实现老鼠的组件,需要我们自己写。”
皮皮:“那我们赶紧写一个老鼠组件吧。”
我:“没问题的,但在这之前,你得先搞明白什么是组件,它的运行机制。”

4.2 Component——组件

Component,组件,顾名思义,就是游戏物体的组成部件。
我拿出扫地机器人,对着皮皮说:“你看这个扫地机器人,它会寻路移动、扫地、避开障碍物、充电等等,它之所以具备这些功能,是因为它有轮子、边刷、激光测距传感器、电源等等这些组件,这些组件拼装在一起通过合理的协同工作,才完整地构成了一台扫地机器人,游戏物体也一样。”
我打开Unity工程,然后选中Main Camera节点,接着说:“看这里,这个Main Camera,它是一个游戏对象,它身上挂了三个组件:TransformCameraAudioListener。”

在这里插入图片描述

皮皮:“那一只老鼠要挂多少组件呢?”
我:“这个要看需求和设计,你可以一个组件只实现一个单一的功能,然后挂很多组件,也可以把多个功能合理地整合在一个组件里,只挂这个组件。顺便说一句,所有物体都必须带一个Transform组件。”
皮皮似懂非懂地点点头,我摸摸皮皮的小脑袋,接着说:“还有,组件也不能滥用,新手容易犯的一个错误就是组件泛滥。优秀的程序员,会懂得合理地使用设计模式,优雅地抽象、封装,把代码打理得干净漂亮,你们喵星人都自带洁癖属性,我觉得你会是一个优秀的程序员的。”
皮皮:“是不是所有的脚本都是Component组件?”
我:“不是的,你注意看,我们创建的脚本,认是继承MonoBehavIoUr的。”

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehavIoUrScript : MonoBehavIoUr
{
    // Start is called before the first frame update
    void Start()
    {
       
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

皮皮:“Component组件,和这个MonoBehavIoUr又是什么关系呢?”
我:“问的好,这里就要介绍一下Unity中的类继承关系了。”
我拿出一张白纸,在纸上画了Unity中的部分类继承关系。

在这里插入图片描述


Unity中,所有实体最上层的基类是ObjectComponent继承ObjectBehavior继承ComponentMonoBehavior继承Behavior

注意这里的Object是指UnityEngine.Object,有别于.NetSystem.object。事实上,UnityEngine.Object也是一个System.object,但一个System.object不一定是一个UnityEngine.Object
比如:
string a = "hello cat";
a一个System.object,但它不是一个UnityEngine.Object

皮皮:“晕了晕了,简直就是俄罗斯套娃啊。”
我:“哈哈,所以呢,继承MonoBehavIoUr的脚本就是Component组件,Unity封装的MonoBehavIoUr还提供了协程的功能,这个后面我再教你。”
我打开Visual Studio继续说,:“在实际开发过程中,我们也会创建一些不是继承MonoBehavIoUr的脚本,这些脚本就不是Component,比如我现在封装一个静态的工具类:GameUtils。”

using System;

public class GameUtils 
{
    /// <summary>
    /// 获取当前系统时间,例:2020/11/21 16:46:29
    /// </summary>
    /// <returns></returns>
    public static string GetNowTime()
    {
        return DateTime.Now.ToString();
    }
}

GameUtils.cs一个脚本,但它不是一个组件。只有组件才可以AddComponent(挂组件)到物体上,同时,组件要挂到物体上才能真正被运行起来。

4.3 MonoBehavIoUr脚本的生命周期

皮皮:“你刚刚说组件要挂到物体上才能真正被运行起来,那它是怎么运行的呢?”
我:“这里就要讲讲MonoBehavIoUr脚本的生命周期了。”
可以参见Unity官方文档的说明:
https://docs.unity3d.com/Manual/ExecutionOrder.html

在这里插入图片描述


原图地址:https://docs.unity3d.com/uploads/Main/monobehaviour_flowchart.svg
皮皮:“铲屎官,郑现在再封你一个官!”
我:“嗯?”
皮皮:“翻译官。”
我:“我挑重点,一个一个函数来吧。”

4.3.1 Reset()函数

Reset,重置函数,这个函数只有在Editor环境下才会被调用,运行过程中是不会调用Reset的。
它是在用户点击Inspector面板上Reset按钮或者首次添加该组件时被调用Reset最常用于在Inspector面板中给定一个认值。

4.3.2 Awake()函数

Awake,唤醒函数,注意它只会被执行一次,这个要牢记。当游戏对象被创建的时候,游戏对象绑定的脚本会在该帧内执行Awake函数

如果脚本所挂的物体最开始不是激活状态,则Awake函数此时不会被调用,激活物体时才会被调用。在Inspector窗口的最左上方,有个勾选,如果不勾选,则表示隐藏物体。
如下,物体处于隐藏状态,此时运行UnityNewBehavIoUrScript脚本中的Awake函数并不会被调用,激活物体时,Awake函数才会被调用

在这里插入图片描述


值得一提的是,如果物体是激活状态,然后脚本是非激活状态,此时运行UnityNewBehavIoUrScript脚本中的Awake函数是会被调用的。
如下,Awake函数照样会被执行。

在这里插入图片描述

4.3.3 OnEnable()函数

OnEnable,激活函数,当脚本激活时被调用,激活一次就调用一次,激活n次,OnEnable调用n次。调用顺序在Awake之后。

4.3.4 Start()函数

Start,启动函数Start函数调用位于Awake函数之后,Start函数会在Update函数第一帧之前被调用,与Awake函数一样只会被调用一次,与Awake函数不一样的是当脚本最开始处于不激活状态,Awake函数会被调用,而Start函数不会被调用,只有激活了脚本才会调用Start函数

4.3.5 Update()函数

Update,帧更新函数,每一帧都会被调用。只有物体和脚本都处于激活状态下才会被调用调用顺序在Start函数之后,之后就每帧调用Update函数,帧与帧之间的时间间隔不是固定的,收到运行时的cpuGPU等工作负荷的影响,我们常说的卡顿,就是运行负荷太高了,此时帧与帧之间的间隔时间就会变大,出现掉帧的情况。

4.3.6 LateUpdate()函数

LateUpdate,延迟更新函数LateUpdate函数会在Update函数结束之后被调用LateUpdate常用为第三人称视角相机跟随,比如在Update中处理主角的移动,在LateUpdate中处理摄像机跟随移动。

4.3.7 FixedUpdate()函数

FixedUpdate,固定更新函数,在游戏运行的过程中,每一帧的处理时间是不固定的,当我们需要固定间隔时间执行某些代码时,就会用到FixedUpdate函数
FixedUpdate调用时间间隔,可以在编辑器中设置。点击菜单Edit - Project Settings...,然后点击Time,可以看到有个Fixed timestep,它就是FixedUpdate调用时间间隔,认是0.02秒

在这里插入图片描述

4.3.8 OnGUI()函数

OnGUI,绘制界面函数。一般作为测试功能使用,如创建测试按钮。
例:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehavIoUrScript : MonoBehavIoUr
{
    private void OnGUI()
    {
        if(GUILayout.Button("我是一个测试按钮"))
        {
            Debug.Log("我被点击啦");
        }
    }
}

在这里插入图片描述

4.3.9 Ondisable()函数

Ondisable,当脚本被禁用时调用。在Inspector窗口中,脚本前面的勾选框,就可以激活和禁用脚本。它与OnEnable函数是对应的。

在这里插入图片描述

4.3.10 OnDestroy()函数

OnDestroy,当脚本所在的物体被销毁或者脚本本身被移除的时候,OnDestroy函数调用

4.4 测试生命周期

皮皮:“感慨呀,每一个脚本都是一条生命呀。”
我:“人生苦短,莫要虚度年华,来,跟着我跑一遍测试脚本,我们来跑一下脚本组件的生命周期测试。”
示例代码

using UnityEngine;

public class NewBehavIoUrScript : MonoBehavIoUr
{
    private bool hasPrintUpdate = false;
    private bool hasPrintLateUpdate = false;
    private bool hasPrintFixedUpdate = false;
    private bool hasPrintGUI = false;

    void Awake()
    {
        Debug.Log("---Awake---");
    }

    void OnEnable()
    {
        Debug.Log("---OnEnable---");
    }

    void Start()
    {
        Debug.Log("---Start---");
    }

    void FixedUpdate()
    {
        if (!hasPrintFixedUpdate)
        {
            hasPrintFixedUpdate = true;
            Debug.Log("---FixedUpdate---");
        }
    }

    void Update()
    {
        if (!hasPrintUpdate)
        {
            hasPrintUpdate = true;
            Debug.Log("---Update---");
        }
    }

    void LateUpdate()
    {
        if (!hasPrintLateUpdate)
        {
            hasPrintLateUpdate = true;
            Debug.Log("---LateUpdate---");
        }
    }

    void OnGUI()
    {
        if (!hasPrintGUI)
        {
            hasPrintGUI = true;
            Debug.Log("---OnGUI---");
        }
    }

    void Ondisable()
    {
        Debug.Log("---Ondisable---");
    }

    void OnDestroy()
    {
        Debug.Log("---OnDestroy---");
    }
}

运行结果如下:

---Awake---
---OnEnable---
---Start---
---FixedUpdate---
---Update---
---LateUpdate---
---OnGUI---
---Ondisable---
---OnDestroy---

《学Unity的猫》——第五章:规范Unity的工程目录结构

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐