10.生命周期函数和打印信息

10.Unity脚本基础-生命周期函数


10.1 知识点

了解帧的概念

  • Unity 底层已经帮助我们做好了死循环。
  • 我们需要学习 Unity 的生命周期函数。
  • 利用它做好的规则来执行我们的游戏逻辑就行了。

生命周期函数的概念

  • 所有继承 MonoBehaviour 的脚本最终都会挂载到 GameObject 游戏对象上。
  • 生命周期函数就是该脚本对象依附的 GameObject 对象从出生到消亡整个生命周期中会通过反射自动调用的一些特殊函数。
  • Unity 帮助我们记录了一个 GameObject 对象依附了哪些脚本,会自动的得到这些对象,通过反射去执行一些固定名字的函数。

生命周期函数

  • Awake

    • 当对象(自己这个脚本类对象)被创建时才会调用 Awake。
    • 类似构造函数的存在,我们可以在一个类对象创建时进行一些初始化操作。
  • OnEnable

    • 当一个对象被激活时,进行一些逻辑处理就可以写在 OnEnable。
    • 运行游戏时,因为脚本挂载对象默认激活,所以会自动调用一次 OnEnable。
  • Start

    • 用于初始化信息的逻辑可以写在 Start,但相对于 Awake 来说,要晚一点,因为它是在对象进行第一次帧更新之前才会执行的。
  • FixedUpdate

    • FixedUpdate 主要用于进行物理更新,是每一物理帧的执行的,但这里的帧是物理帧和游戏帧有点不同。
  • Update

    • Update 主要用于处理游戏核心逻辑更新,每一游戏帧都会执行。
  • LateUpdate

    • LateUpdate 一般用来处理摄像机位置更新相关内容,会比 Update 晚一些执行。
  • OnDisable

    • 如果希望在一个对象失活时做一些处理,可以在 OnDisable 中写逻辑。
  • OnDestroy

    • 当对象被销毁时调用 OnDestroy,销毁对象时会调用,而且调用 OnDestroy 前对象也算失活,也会调用一次 OnDisable。

生命周期函数支持继承多态

  • 新建一个 Lesson10_生命周期函数Son 脚本继承 Lesson10_生命周期函数 脚本,在 Lesson10_生命周期函数 脚本中把 Awake 添加 protectedvirtual 修饰符,在 Lesson10_生命周期函数Son 脚本重写 Awake,挂载到新的一个对象上。
public class Lesson10_生命周期函数Son : Lesson10_生命周期函数
{
    protected override void Awake()
    {
        base.Awake();
        print("子类的 Awake");
    }
}

在Unity中打印信息

Debug类中的打印信息方法

Log方法 - 打印普通信息
Debug.Log("Awake Debug.Log");
LogWarning方法 - 打印警告信息
Debug.LogWarning("Awake Debug.LogWarning");
LogError方法 - 打印报错信息
Debug.LogError("Awake Debug.LogError");

MonoBehaviour类中的打印信息方法

print方法 - 打印普通信息

当继承了MonoBehaviour类时,可以使用该类的print方法打印信息。

print("Awake print");

这些方法用于在调试过程中输出不同级别的信息,方便开发者定位和解决问题。

总结

  • 这些生命周期函数,如果你不打算在其中写逻辑,那就不要在这些出生命周期函数,因为 Unity 会根据反射去查看有没有生命周期函数,假如没有就不管他了,有的话又不在里面写逻辑会造成额外的开销。

10.2 知识点代码

Lesson10_生命周期函数

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

public class Lesson10_生命周期函数 : MonoBehaviour
{
    #region 知识点一 了解帧的概念
    //Unity 底层已经帮助我们做好了死循环
    //我们需要学习Unity的生命周期函数
    //利用它做好的规则来执行我们的游戏逻辑就行了
    #endregion

    #region 知识点二 生命周期函数的概念
    //所有继承MonoBehavior的脚本 最终都会挂载到GameObject游戏对象上
    //生命周期函数 就是该脚本对象依附的GameObject对象从出生到消亡整个生命周期中
    //会通过反射自动调用的一些特殊函数

    //Unity帮助我们记录了一个GameObject对象依附了哪些脚本
    //会自动的得到这些对象,通过反射去执行一些固定名字的函数
    #endregion

    #region 知识点三 生命周期函数
    //注意:
    //生命周期函数的访问修饰符一般为private和protected
    //因为不需要再外部自己调用生命周期函数 都是Unity自己帮助我们调用的

    //当对象(自己这个脚本类对象)被创建时 才会调用Awake
    //类似构造函数的存在 我们可以在一个类对象 该创建 进行一些初始化操作
    protected virtual void Awake()
    {
        //在Unity中打印信息的两种方式
        //1. 没有继承MonoBehaviour类的时候 调用Debug类的方法打印信息 可以打印普通信息,警告信息,报错信息
        //Debug.Log("Awake Debug.Log");
        //Debug.LogWarning("Awake Debug.LogWarning");
        //Debug.LogError("Awake Debug.LogError");
        //2. 继承了MonoBehavior 调用MonoBehavior类的print方法打印信息
        print("Awake print");
    }

    //当一个对象被激活时 进行一些逻辑处理 就可以写在OnEnable
    void OnEnable()
    {
        print("OnEnable");
    }

    //用于初始化信息的逻辑可以写在Start 但是它相对Awake来说 要晚一点
    //因为它是在对象 进行第一次帧更新之前才会执行的
    void Start()
    {
        print("Start");
    }

    //FixedUpdate主要是用于 进行物理更新 
    //FixedUpdate是每一帧的执行的 但是 这里的帧是物理帧 和游戏帧 有点不同
    //FixedUpdate的时间间隔 是可以在 project setting中的 Time里去设置的
    void FixedUpdate()
    {
        print("FixedUpdate");
    }

    //Update主要用于处理游戏核心逻辑更新
    void Update()
    {
        print("Update");
    }

    //LateUpdate一般这个更新是用来处理 摄像机位置更新相关内容的
    //Update和LateUpdate之间 Unity进了一些处理 处理我们动画相关的更新
    void LateUpdate()
    {
        print("LateUpdate");
    }

    //如果我们希望在一个对象失活时做一些处理 就可以在OnDisable中写逻辑
    void OnDisable()
    {
        print("OnDisable");
    }

    //当对象被销毁时调调用OnDestroy
    void OnDestroy()
    {
        print("OnDestroy");
    }

    #endregion

    #region 知识点四 生命周期函数 支持继承多态

    #endregion
}

Lesson10_生命周期函数Son

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

public class Lesson10_生命周期函数Son : Lesson10_生命周期函数
{
    protected override void Awake()
    {
        base.Awake();
        print("子类的Awake");
    }
}

10.3 练习题

请在纸上默写出教的各个生命周期函数,并写出他们分别何时执行,以及执行的先后顺序是什么

  • Awake: 当对象被创建时调用,用于初始化对象,但在所有对象的Start方法之前调用。
  • Start: 在对象第一次更新之前被调用,用于初始化。
  • Update: 在每一帧更新时被调用,用于对象的持续更新。
  • FixedUpdate: 在每一帧的物理系统更新之后被调用,用于物理相关的更新。
  • LateUpdate: 在所有对象的Update方法执行完之后调用,用于处理其他对象的更新后的操作。
  • OnEnable: 当对象被激活时调用,用于处理对象被激活时的操作。
  • OnDisable: 当对象被失活时调用,用于处理对象被失活时的操作。
  • OnDestroy: 当对象被销毁时调用,用于处理对象被销毁时的操作。

请用你的理解说出,生命周期函数并不是基类中的成员,为什么Unity可以自动的执行这些特殊的函数?

Unity能够自动执行生命周期函数的原因在于其内部实现了对游戏对象和脚本的管理和调度。Unity在运行时会维护场景中的所有游戏对象及其关联的脚本对象,并且知道何时何处需要调用这些生命周期函数。通过对脚本对象的反射,Unity能够根据生命周期函数的命名约定,在特定的时机自动调用对应的函数。这样做的好处是开发者不需要手动管理对象的生命周期,只需按照Unity的命名规范编写相应的函数即可,简化了开发流程,提高了开发效率。

关于继承Mono的类的构造函数

虽然不建议在继承MonoBehaviour的类中编写构造函数,但即便如此,我们仍然可以在这些类中编写构造函数。在编辑模式下或者运行后,只要该脚本挂载在场景中,无参构造函数会被自动执行。这是因为Unity利用反射机制帮助我们实例化了该脚本对象。

为什么不建议写构造函数呢?

  1. Unity规定,继承MonoBehaviour的脚本不能通过new关键字实例化,只能通过挂载到游戏对象上来创建对象。
  2. Awake生命周期函数在对象创建后会自动调用,类似于构造函数的存在,因此可以在其中进行初始化操作。
  3. 编写构造函数会破坏Unity的设计规范。

总结:

如果需要进行初始化操作,可以在AwakeStart生命周期函数中进行。理解这两个函数的执行时机,根据需求选择在何处进行初始化。切记,不要在继承MonoBehaviour的类中使用new关键字创建对象。

不同对象的生命周期函数是在同一个线程中执行的吗?

是的,Unity中所有对象上挂载的生命周期函数都是在一个主线程中按顺序执行的。Unity会在主线程的循环中按照顺序执行场景中所有对象上挂载的脚本的对应生命周期函数。



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏