脚本的生命周期
目录
- 脚本生命周期图
- Update()、Start()、Awake()简介
- 脚本的协程
Update、Start、Awake
- FixedUpdate()
固定更行 - Update()
更新 - LateUpdate()
晚于更新
• 其中,FixedUpdate()和Update(),在MonoBehavior启用时,在每一帧都被调用,都是用来更新的。
但是Update()每一帧的时间不固定,即第一帧与第二帧的时间t1和第三帧与第四帧的时间t2不一定相同。FixedUpdate()每帧与每帧之间相差的时间是固定的。Update受当前渲染的物体影响,这与当前场景中正在被渲染的物体有关(比如人物的面数,个数等),时快时慢,帧率会变化,所有Update被调用的时间间隔就会发生变化。但是FixedUpdate则不受帧率的变化影响,它以固定的时间间隔来被调用。
所以一些物理属性的操作应该放在FIxedUpdate中进行操作,比如Force,collider,Rigidbody等,以及键盘鼠标的输入输出Input。这样可以让GameObject的物理表现更平滑,更加接近现实。其中FixedUpdate的时间间隔可以在Edit->Project Setting->time->Fixed timestep 中进行修改。
• 其中Update和LateUpdate
LateUpdate是在所有的Update函数调用完后被调用。可以用于调整脚本执行顺序。例如:当在Update中设置物体的移动时,就可以用LateUpdate来实现跟随物体的相机。因为Update的先后顺序是随机的,如果相机先Update而物体还未移动,则下一帧物体先Update时,就会出现瞬移的情况;以及物体之间跟随的情况,如果跟随的物体使用Update来更新的话,跟随的物体就容易出现抖动。如果是在LateUpdate中跟随的话就只会等所有的Update执行完成后的最后位置再进行更新,就防止了抖动。
Update和LateUpdate不适合用于更新频率要求比较稳定的物理系统。 - Awake()
在脚本实例化的时候就会被调用(不论是否enable),在脚本的生命周期中只会被调用一次。 - Start()
Start是脚本第一次enable之后、在Update之前调用的,Start在脚本的生命周期中也只可能被调用一次。
• Awake是在所有对象实例化之后,所以可以在Awake中给各个组件之间添加引用关系。但是不同对象之间的Awake的顺序是随机的。
• Start需要在enable之后,所以通过Start和Awake的顺序关系,可以把初始化的操作放在Awake中,这样可以保证Start开始之前,所需要的初始化操作是已经完成的了。
脚本的协程
脚本更新
Unity只支持单线程,但是可以通过C#来模拟多线程。
举例来说:现在需要每1秒就创建一个游戏对象,可以通过协程的概念,使用for循环来写。使用StartCoroutine()方法即可启用一个协程任务。在循环中,使用yield return 来告诉unity需要等待多久才执行下一个循环。
//每隔一秒新建一个cube
public class Script_04_04 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine(CreateCube());
}
IEnumerator CreateCube()
{
for(int i = 0; i < 10; i++)
{
GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = Vector3.one * i;
yield return new WaitForSeconds(1f);
}
}
}
但是协程代码在实际应用中容易出错,最好不要大量使用。
停止协程任务
在协程任务启动的过程中,如果需要重新启动它,必须停掉之前的协程。使用StartCoroutine()返回这个协程对象,需要停止的时候使用StopCoroutine()。
//每隔一秒新建一个cube
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Script_04_04 : MonoBehaviour
{
IEnumerator CreateCube()
{
for(int i = 0; i < 10; i++)
{
GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = Vector3.one * i;
yield return new WaitForSeconds(1f);
}
}
private Coroutine m_Coroutine = null;
private void OnGUI()
{
if (GUILayout.Button("startcoroutine"))
{
if (m_Coroutine != null)
{
StopCoroutine(m_Coroutine);
}
m_Coroutine = StartCoroutine(CreateCube());
}
if (GUILayout.Button("stopcoroutine"))
{
if (m_Coroutine != null)
{
StopCoroutine(m_Coroutine);
}
}
}
}
使用OnGUI显示FPS
FPS的含义就是一秒钟Update被执行了多少次。所以只需要在Update()中获取每一秒所执行的次数,最终在OnG()方法中将FPS打印在平面左上角。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Script_04_06 : MonoBehaviour
{
public float updateInterval = 0.5F;
private float accum = 0;
private int frames = 0;
private float timeleft;
private string stringFps;
void Start()
{
timeleft = updateInterval;
}
void Update()
{
timeleft -= Time.deltaTime;//deltaTime为运行一帧所需要的时间
accum += Time.timeScale / Time.deltaTime;//timeScale为运行速度
++frames;
if (timeleft <= 0.0)
{
float fps = accum / frames;
string format = System.String.Format("{0:F2} FPS", fps);
stringFps = format;
timeleft = updateInterval;
accum = 0.0f;
frames = 0;
}
}
private void OnGUI()
{
GUIStyle gUIStyle = GUIStyle.none;
gUIStyle.fontSize = 30;
gUIStyle.normal.textColor = Color.red;
gUIStyle.alignment = TextAnchor.UpperLeft;
Rect rt = new Rect(40, 0, 100, 100);
GUI.Label(rt, stringFps, gUIStyle);
}
}
tips:1:“timeScale不会影响Update和LateUpdate的执行速度”
2:“FixedUpdate是根据时间来的,所以timeScale只会影响FixedUpdate的速度”
参考文献 《Unity3D游戏开发 第二版》