U1L2——生命周期函数

本章代码关键字

1
2
3
4
5
6
7
8
9
MonoBehaviour        //脚本内继承该类且脚本与继承该类的类名一致才能使脚本挂载到一些对象上,注意这种类不能new出来,也不再建议写构造函数
Awake() { } //当一个对象被创建了,awake()会立刻执行
OnEnable() { } //想要当一个对象被激活的时候,进行一些逻辑处理就可以写这个函数
Start() { } //与awake一样,都是对象在被创建出来后只会调用一次的函数,区别在于运行时机不同
FixedUpdate() { } //主要用于进行物理更新的函数
Update() { } //主要用于处理游戏核心逻辑更新的函数
LateUpdate() { } //一般是用来处理摄像机位置更新相关内容的函数
OnDisable() { } //如果希望在一个对象每次失活时做一些处理 就可以在该函数中写逻辑
OnDestroy() { } //对象被销毁时调用 依附的GameObject对象被删除时(该组件被移除也会调用,只会调用一次)

生命周期函数的概念

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

Unity中所有对象上挂载的生命周期函数都是在一个主线程中按先后执行的
Unity会主动把场景上的对象,对象上挂载的脚本都统统记录下来,
在主线程的死循环中,按顺序按时机的通过反射,执行记录的对象身上挂载的脚本的对应生命周期函数

注意!

生命周期函数的访问修饰符一般为 private​ 和 protected
因为不需要在外部调用生命周期函数 都是 Unity 自己帮我们调用的

当对象(自己这个类的对象,也就是脚本)被创建时 才会调用该生命周期函数
接下来所说的生成,激活,失活,销毁,是相对于挂载到unity对象上的脚本对象说的
类似于构造函数的存在 我们可以在一个类对象 刚创建时进行一些初始化操作

这些生命周期函数 如果我们不打算在其中写逻辑 那就不需要再写出该生命周期函数!(因为脚本由反射得到代码执行,多余的生命周期函数会有性能上的开销)
这些脚本可以挂载到任意的对象上,但是不同的对象挂载相同的脚本 实例化的是不同的对象,这是各管各而不是共享的
哪怕是一个对象挂载了重复的同一个脚本,也是实例化出不同的对象

切记!!继承MonoBehaviour的脚本不要new,不要new,不要new!!


生命周期函数

生命周期函数按照执行顺序依次有:

1
2
3
4
5
6
7
8
Awake() { }         //当一个对象被创建了,awake()会立刻执行
OnEnable() { } //想要当一个对象被激活的时候,进行一些逻辑处理就可以写这个函数
Start() { } //与awake一样,都是对象在被创建出来后只会调用一次的函数,区别在于运行时机不同
FixedUpdate() { } //主要用于进行物理更新的函数
Update() { } //主要用于处理游戏核心逻辑更新的函数
LateUpdate() { } //一般是用来处理摄像机位置更新相关内容的函数
OnDisable() { } //如果希望在一个对象每次失活时做一些处理 就可以在该函数中写逻辑
OnDestroy() { } //对象被销毁时调用 依附的GameObject对象被删除时(该组件被移除也会调用,只会调用一次)

Awake()

当对象(自己这个类的对象,也就是脚本)被创建时 才会调用该生命周期函数
接下来所说的生成,激活,失活,销毁,是相对于挂载到unity对象上的脚本对象说的
类似于构造函数的存在 我们可以在一个类对象 刚创建时进行一些初始化操作

1
2
3
4
5
6
7
8
9
10
protected virtual void Awake()
{
//在Unity中打印信息的两种方式
//1、当没有继承MonoBehaviour类时
Debug.Log("Start!!!");
//Debug.LogError("出错了");
//Debug.LogWarning("警告");
//2、当继承了MonoBehaviour 有一个线程的方法可以使用
print("Awake");
}

OnEnable()

想要当一个对象被激活(从失活恢复到激活状态也会触发)的时候,进行一些逻辑处理,就可以写这个函数

1
2
3
4
void OnEnable()
{
print("OnEnable");
}

Start()

awake()​一样,都是对象在被创建出来后只会调用一次的函数,但是,两者仍有区别,区别在于两者的执行时机不同
例如,如果在帧更新时,一个对象被创建了,awake()​会立刻执行,而start()​则会在本帧循环结束后,下一帧开始前执行,
也就是该对象第一次帧更新之前执行

1
2
3
4
void Start()
{
print("Start");
}

FixedUpdate()

这个主要用于 进行物理更新,它是每一帧的执行的 但是 这里的物理帧 和 游戏帧 有点不同
它的时间间隔 是可以在Project Setting中的Time里去设置的

image

1
2
3
4
void FixedUpdate()
{
print("FixedUpdate");
}

Update()

主要用于处理游戏核心逻辑更新的函数

1
2
3
4
void Update()
{
print("Update");
}

LateUpdate()

一般这个更新是用来处理 摄像机位置更新相关内容的
Update​ 和 LateUpdate​之间,Unity进行了一些处理 处理动画相关的更新

1
2
3
4
void LateUpdate()
{
print("LateUpdate");
}

OnDisable()

如果我们希望在一个对象每次失活时做一些处理 就可以在该函数中写逻辑

1
2
3
4
void OnDisable()
{
print("OnDisable");
}

OnDestroy()

对象被销毁时调用 依附的GameObject对象被删除时(该组件被移除也会调用,只会调用一次)

1
2
3
4
void OnDestroy()
{
print("OnDestroy");
}

此外、还有碰撞检测函数、OnGUI()等这种特殊的生命周期函数


生命周期函数支持多态

类可以继承,且遵循多态,可以vob重写

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Lesson1 : MonoBehaviour
{
protected virtual void Awake()
{
//在Unity中打印信息的两种方式
//1、当没有继承MonoBehaviour类时
Debug.Log("Start!!!");
//Debug.LogError("出错了");
//Debug.LogWarning("警告");
//2、当继承了MonoBehaviour 有一个线程的方法可以使用
print("Awake");
}
}
1
2
3
4
5
6
7
8
public class Lesson1Son : Lesson1
{
protected override void Awake()
{
base.Awake();
print("子类的Awake");
}
}

关于MonoBehaviour的类中的构造函数

我们要知道,虽然建议大家不在继承MonoBehaviour​的类中写构造函数
但是不意味着我们不能写,当我们在继承MonoBehaviour​的类中写无参构造函数时,
你会发现在编辑模式下或者运行后,只要该脚本挂载在场景中,那么该无参构造函数是会被自动执行的。

因为Unity的工作原理中提到的反射机制,Unity实际上通过反射帮助我们实例化了该脚本对象,既然要实例化那么肯定是需要new​的,
只不过Unity中不需要我们自己new​继承了MonoBehaviour​的类,只要挂载后Unity帮助我们做了这件事。
那么为什么不建议大家写构造函数呢?

  1. Unity的规则就是,继承MonoBehaviour的脚本不能new​只能挂载
  2. 生命周期函数的Awake​是类似构造函数的存在,当对象出生就会自动调用
  3. 写构造函数反而在结构上会破坏Unity设计上的规范

总结:
如果继承MonoBehaviour​的脚本想要进行初始化相关,可以在Awake或者Start中进行,
搞清这两个生命周期函数的执行时机,根据需求选择在哪里进行初始化。
切记!!继承MonoBehaviour的脚本不要new,不要new,不要new!!