Component 组件脚本及其基本生命周期

2017年03月17日 15:17 0 点赞 0 评论 更新于 2025-11-21 21:15

3.1 组件(Component)

组件是 Unity 中最核心的概念,也是一切编程的基础。没有组件,就无法进行 Unity 编程。

打开一个新的 Unity 工程,在 Project 面板中右键可以直接创建一个 C# 脚本。默认脚本的内容如下:

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {
// Use this for initialization
void Start () {

}

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

}
}

默认的脚本继承自 MonoBehaviour 类,这是自定义脚本组件通常继承的类,即我们自己编写的脚本的父类。而 Unity 内部组件,如相机等,继承自 MonoBehaviour 的父类 Behavior 或者更上层的父类 Component

Unity 为何要分成三个级别继承呢?从 ComponentBehavior 只是增加了一个是否可以 enable 的属性,用于区分有些组件可以禁用,而有些组件不可以。从 BehaviorMonoBehaviour,则是专门为 Unity 程序员准备的,因为它增加了很多响应消息,包括上述代码中看到的 StartUpdate 以及后面会提到的 LateUpdateFixedUpdate 等消息。这些消息是为了让程序员可以方便地控制和响应组件,而 Unity 内置组件不需要这些消息,因为它们内部知道何时需要进行启动、更新等操作。

我们尝试参考 MonoBehaviour 的文档,将常见的消息响应全部打印到控制台上,代码如下:

using UnityEngine;
using System.Collections;
using Assets.AndrewBox.Util;

public class TestComponenets : MonoBehaviour {
void Awake() {
Debuger.LogAtFrame("Awake");
}

void Start () {
Debuger.LogAtFrame("Start");
}

// Update is called once per frame
void Update () {
// Debuger.LogAtFrame("Update");
}

void LateUpdate() {
// Debuger.LogAtFrame("LateUpdate");
}

void FixedUpdate() {
// Debuger.LogAtFrame("FixedUpdate");
}

void OnEnable() {
Debuger.LogAtFrame("OnEnable");
}

void OnDisable() {
Debuger.LogAtFrame("OnDisable");
}

void OnDestroy() {
Debuger.LogAtFrame("OnDestroy");
}
}

附加的 Debuger 类用于打印消息,它在显示消息的同时,记录了当前画面运行的帧数,便于我们观察函数调用的次序和时机:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Assets.AndrewBox.Util {
public static class Debuger {
public static void LogAtFrame(string infor) {
Debug.Log("[" + Time.frameCount + "]" + infor);
}
}
}

准备好代码之后,在场景中新建一个 Cube(实际上任意 GameObject 都可以),将 TestComponenets 脚本拖放到该 Cube 上。然后启动运行程序,并在 Cube 的 TestComponenets 组件上,将勾选状态关闭再打开,此时可以在控制台看到输出内容。之后停止运行,去掉上述代码中的注释,使几个 Update 方法暴露出来,再次运行程序查看结果。

最终,我们可以得出如下结论:

  • Awake 方法:当 GameObject 被启用时,该方法立刻执行。从中文字面意思理解,组件已经“苏醒”,但还未执行,只是做好了准备。该方法只执行一次。
  • OnEnable 方法:当组件被启用时(前提是 GameObject 已启用),该方法立刻执行,多次启用时会反复执行。
  • OnDisable 方法:与 OnEnable 方法对应,当组件被禁用时,该方法立刻执行,多次禁用时会反复执行。
  • Start 方法:组件被启用后的下一帧才会执行,且只执行一次。特别注意,这里是下一帧,如果不注意,在资源加载方面可能会出现问题。
  • OnDestroy 方法:当组件被销毁时执行。
  • Update 方法:每帧执行一次,每秒刷新次数取决于硬件图像的刷新速度。
  • LateUpdate 方法:每帧执行一次,在 Update 方法之后执行,一般用于绘制到屏幕的最后处理(如果没有此特殊需求,使用 Update 方法即可)。
  • FixedUpdate 方法:默认每隔 0.02 秒(具体时间可以设置)执行一次,与图像刷新率无关,用于物理逻辑计算。

正常情况下,执行顺序如下:

也就是说,在 AwakeOnEnableStart 方法执行之后,开始几种 Update 循环。

一般来说,OnEnable 方法用于处理开启和关闭组件时的开关量转换,而组件的初始化可以写在 AwakeStart 方法中。

由于 Awake 方法在加载和启用 GameObject 后立刻执行,因此,如果本组件跟随 GameObject 加载后,应该立刻初始化本组件的公共成员(如果这些成员需要被其他代码访问)。因为如果在 Start 方法中初始化这些成员,需要等待一帧,而在这一帧过程中,很可能已经发生了对这些公共成员的访问,此时成员尚未初始化。所以应该避免这种情况的发生。

我们暂时将这种初始化方式称为二阶段初始化,以便更好地记忆。在后续的章节中会有更多体现。

原文链接:http://blog.csdn.net/andrewfan

作者信息

孟子菇凉

孟子菇凉

共发布了 3994 篇文章