Awake和其他生命周期的区别

Awake调用的时机

脚本Awake的内容只要挂载它的物体在场景中就能启用。

脚本StartUpdateLateUpdate等的内容需要当前脚本的enable打开才能启用(这种脚本挂载时前面有一个“√”)

OnEnable、OnDisable调用时机

当一个脚本第一次调用时,先执行OnEnable再执行Start。之后脚本再次enable或disable只会执行OnEnableOnDisable,不会调用Start

FixedUpdate的Timestep

FixedUpdate为固定帧率,在Project Settings——Time里面可以设定Timestep,默认0.02即为50帧

LateUpdate

一般用作摄像机跟随运算

FixedUpdate的调用

FixedUpdate并不是一直保证每0.02秒运行一次,我们可以用下面的代码来测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using UnityEngine;

namespace DarknessWarGodLearning.Test
{
public class FixedUpdateTest : MonoBehaviour
{
private float lastFixedUpdate = 0f;
private float lastUpdateDuration = 0f;

private void FixedUpdate()
{
var cur = Time.realtimeSinceStartup;
lastUpdateDuration = cur - lastFixedUpdate;
lastFixedUpdate = cur;
Debug.Log(lastUpdateDuration.ToString());
}
}
}

如果我们输出Debug.Log("FixedDeltatime:" + Time.fixedDeltaTime.ToString());它会固定是0.02秒

FixedUpdate保证的是它的计算是和物理系统的计算同步,而Update保证的是它的计算和渲染同步。

我们在Unity - Manual: Order of execution for event functions中可以看到,FixedUpdate是个独立的更新循环。

在Update执行1次渲染帧之间,FixedUpdate可能执行了0次、1次或许多次物理帧。假设我们的timestep设为0.02,FixedUpdate就要保证每一秒至多有50帧物理帧。

我们先假设物理系统执行非常顺利,而渲染比较卡的情况:

  • 这种情况下一个渲染帧需要的时间(Time.DeltaTime)可能会超过物理帧规定的时间(Time.fixedDeltaTime),这个时候就会出现一帧渲染帧之间,FixedUpdate执行了多次的情况

我们再假设渲染系统执行顺利,而物理系统比较卡的情况:

  • 在这种情况下,如果在一个规定的物理帧(每0.02秒)中没有完成物理运算,那么执行渲染帧是没有意义的,所以Unity在Project Settings——Time里面添加了Maximum Allowed Timestep,默认是0.3333333,因为更高的timestep有更小的物理计算压力,可以保证在渲染时物理计算可以完成。
  • 如果还是完不成,Unity会暂停物理计算,进行渲染,再在下一个物理帧时从暂停的地方继续运算,这样虽然会导致物理运算变慢,不过是可以接受的。

脚本执行顺序

在游戏中尽量在入口物体GameRoot中进行全局初始化,以保证初始化顺序,也就是说只有GameRoot脚本有StartAwake方法,其他功能性脚本的初始化从Init函数中进行。

GameRoot

1
2
3
4
5
6
7
8
9
public class GameRoot : MonoBehaviour{
private void Start(){
ModuleA mdA = GetComponent<ModuleA>();
ModuleB mdB = GetComponent<ModuleB>();

mdA.Init();
mdB.Init();
}
}

逻辑帧

Unity一个场景中的所有物体是在一个单线程(即主线程)串行更新的,在一次帧循环中,场景中的物体一个接着一个地完成各自的生命周期内的函数(实际情况并不完全是这样,在这里为了简化理解),我们参考场景其中一个物体,从它本身开始Update再回到它本身下一次Update的过程称为一次逻辑帧。

协程的一些要点

StopAllCoroutines只能停止当前脚本内的协程。

StopCoroutine()有多个重载,既能使用Coroutine作为参数,又能使用IEnumerator作为参数,还能使用字符串(传入方法名)

如果开启协程的物体,在协程中途Disable或Destroy,那么此物体的协程都会关闭,重新Enable不会继续协程