33.性能优化-CPU-脚本-协程
33.1 知识点
协同程序的额外开销
上节课的 Update 优化里提到,可以通过降低 Update 的执行频率来提升效率,很容易想到用协程来实现(例如 yield return new WaitForSeconds(0.25f) 再执行逻辑)。当时没有推荐协程,主要是因为协程本身会带来额外开销。
与标准函数调用相比:
- 启动协程有额外成本,大约相当于标准函数调用的 3 倍左右。
- 启动协程会额外分配内存,用于保存当前协程的状态。
- 协程里使用 yield 时,每次执行到 yield(如
WaitForSeconds、WaitForEndOfFrame),都会再次产生类似的调度与状态维护开销。
因此在使用协程时,要权衡:这些成本是否小于它带来的便利(如代码可读性、少写计时变量等),再决定是否用协程。
利用 Update 替代协程处理时间
某些只跟时间相关的逻辑(例如倒计时、隔一段时间执行一次),完全可以在 Update 里用 Time.deltaTime 累加计时实现,不必开协程,往往更省开销。
private float _timer;
void Update()
{
_timer += Time.deltaTime;
if (_timer >= 1f)
{
// 每秒执行一次的逻辑
_timer = 0f;
}
}
避免协程中逻辑过于复杂
若协程里嵌套调用其他协程、状态跳转很多,维护成本会变高,问题难以追踪、调试也困难。应尽量避免把复杂流程全部塞进协程,必要时用状态机或拆成多个简单协程。
避免大量并发的协程
若场景中大量对象同时启动协程,会带来可观的调度开销和 GC 压力。应避免「每个对象都自己开一堆协程」的做法。
例如:对象需要定时执行某段逻辑时,可以优先用上节课的 Update 优化手段——降低执行频率(Update 里按间隔执行)、自定义 Update 管理器(统一在一个 Update 里驱动),而不是每个对象各起一个协程。
善用 WaitForSecondsRealtime
WaitForSecondsRealtime 与 WaitForSeconds 的区别在于:
- WaitForSecondsRealtime:不受
Time.timeScale影响,按真实时间等待。 - WaitForSeconds:受
Time.timeScale影响,缩放后时间会变快/变慢。
需要「和游戏暂停无关的等待」时(如 UI 动画、真实时间倒计时),用 WaitForSecondsRealtime;需要「跟游戏时间同步的等待」时,用 WaitForSeconds。按需求选择即可。
// 受 Time.timeScale 影响
yield return new WaitForSeconds(1f);
// 不受 Time.timeScale 影响,按真实时间
yield return new WaitForSecondsRealtime(1f);
利用 C# 的 async/await 替代协程
async/await 在性能与可读性上通常优于协程(无额外协程调度、状态机由编译器生成)。但 Unity 自带的不少异步 API 最初是为协程设计的,并不直接返回 Task,无法直接 await。
可以借助第三方库,让原本配合协程使用的 API 也能配合 async/await 使用,例如:
- UniTask(推荐):https://github.com/Cysharp/UniTask
- Unity3dAsyncAwaitUtil:https://github.com/ModestTree/Unity3dAsyncAwaitUtil
在允许引入依赖的前提下,用 UniTask 等替代协程做异步流程,往往能获得更好的性能和更清晰的代码结构。
33.2 知识点代码
Lesson33_性能优化_CPU_脚本_协程.cs
public class Lesson33_性能优化_CPU_脚本_协程
{
#region 知识点一 协同程序的额外开销
/*
* 降低 Update 执行频率时容易想到协程,但协程有额外开销:
* 1. 启动成本约为标准函数调用的 3 倍
* 2. 启动时会额外分配内存保存状态
* 3. 每次 yield 都会再次产生调度与状态维护开销
* 使用协程前要权衡:成本是否小于它带来的好处。
*/
#endregion
#region 知识点二 利用Update替代协程处理时间
/* 基于时间的操作(如倒计时)可在 Update 中配合 deltaTime 实现,无需协程 */
#endregion
#region 知识点三 避免协程中逻辑过于复杂
/* 协程嵌套、状态跳转多时维护成本高、难追踪、调试困难,应尽量避免 */
#endregion
#region 知识点四 避免大量并发的协程
/*
* 大量对象同时启动协程会造成调度开销和 GC 压力。
* 定时逻辑可优先用 Update 优化手段:降低执行频率、自定义 Update 管理器等。
*/
#endregion
#region 知识点五 善用WaitForSecondsRealtime
/* WaitForSecondsRealtime 不受 timeScale 影响,WaitForSeconds 会;按需选择 */
#endregion
#region 知识点六 利用C#的async/await相关方案替代协程
/*
* async/await 性能与可读性通常优于协程。
* Unity 部分 API 不直接支持 Task,可用第三方:UniTask、Unity3dAsyncAwaitUtil 等。
*/
#endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com