Unity 协同程序

Unity 协同程序

Unity 2022.3 官方协同程序文档

部分内容如下
协同程序允许您将任务分散到多帧中执行。在Unity中,协程是一种可以暂停执行并将控制权返回给Unity的方法,但随后在它离开的地方继续执行。

在大多数情况下,当您调用一个方法时,它会运行到完成,然后将控制权返回给调用方法,以及任何可选的返回值。这意味着在方法中发生的任何动作都必须在单个帧更新中发生。

在希望使用方法调用来包含过程动画或随时间变化的事件序列的情况下,可以使用协程。

然而,重要的是要记住协程不是线程。在协程内运行的同步操作仍然在主线程上执行。如果希望减少主线程上花费的CPU时间,避免在协程中阻塞操作与在任何其他脚本代码中一样重要。如果你想在Unity中使用多线程代码,可以考虑使用C# Job System.

如果需要处理长时间的异步操作,比如等待HTTP传输、资产加载或文件I/O完成,那么最好使用协程。
协程是一种方法,您可以声明一个返回类型为IEnumerator的方法,并在程序体中包含yield返回语句。yield返回结果是执行暂停并在下一次执行中继续执行的点。要开始一个协程,需要使用StartCoroutine函数:并且是要继承 MonoBehaviour 的脚本才可以通过 StartCoroutine 开始一个协同程序

 private IEnumerator DelayCall()
 {
     Debug.Log("A:开始执行 DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待一帧(Update)结束,继续执行 yield 下面的逻辑
     yield return new WaitForEndOfFrame();

     Debug.LogError("B:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待一帧(FixedUpdate) 结束,继续执行 yield 下面的逻辑
     yield return new WaitForFixedUpdate();

     Debug.Log("C:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待 1 秒,继续执行 yield 下面的逻辑
     yield return new WaitForSeconds(3);

     Debug.LogError("D:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待 2 秒,继续执行 yield 下面的逻辑
     yield return new WaitForSecondsRealtime(5);

     Debug.LogError("E:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待,直到 WaitUntil 中方法,最后返回 成功,继续执行 yield 下面的逻辑
     yield return new WaitUntil(() =>
     {
         bool result = false;
         // DoSomething
         result = !result;
         return result;
     });

     Debug.LogError("F:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待,直到 WaitWhile 中方法,最后返回 失败,继续执行 yield 下面的逻辑
     yield return new WaitWhile(() =>
     {
         bool result = true;
         // DoSomething
         result = !result;
         return result;
     });

     Debug.LogError("G:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 等待,直到 WWW 网络请求完成、失败,继续执行 yield 下面的逻辑
     yield return new WWW("http://xxx.yyy.zzz");

     Debug.LogError("H:DoSomething");
     // 遇到 yield 将控制权交换给 Unity 主线程,主线程处理别的逻辑
     // 因为 yield 返回 break,中断,不会执行到 I 位置
     yield break;

     // 编译提示检测到无法访问的代码
     Debug.LogError("I:DoSomething");
 }

yield 返回的是继承自 YieldInstruction 、CustomYieldInstruction 的对象,可以根据需求,自定义类

文档内容结合上面代码讲解

开始协同程序的方法
Coroutine coroutine1 = StartCoroutine(DelayCall());
Coroutine coroutine2 = StartCoroutine(“DelayCall”);

注意:此处有一个隐含知识点,只有继承 MonoBehaviour 的脚本才可以 调用 StartCoroutine
如果在一个 没有继承 MonoBehaviour 的 C# 类中如何开启 协同程序?

public class CoroutineTest : MonoBehaviour
{
    public static CoroutineTest instance;
    void Awake()
    {
        instance = this;
    }
}

// 在别的地方可以类似下面这种方式调用 
CoroutineTest.instance.StartCoroutine(DelayCall());

因为协同程序也是在主线程中执行的,所以执行复杂逻辑的时候也会引起主线程的卡顿
如上 在 A 位置逻辑 还没执行完成的时候,依然会卡在主线程
当 A 位置逻辑执行完成,执行到 yield,此时会将控制权 返还给主线程
当 yield 等待结束,开始执行 yield 下面的逻辑
依次类推执行下去

停止协同程序的方法
StopCoroutine(coroutine1);

StopCoroutine(“DelayCall”); // 只能停止通过方法名字符串,调用开始的如 StartCoroutine(“DelayCall”);

StopAllCoroutines()

注意:此处有一个隐含知识点,只有继承 MonoBehaviour 的脚本才可以 调用 StopCoroutine,并且停止的也是通过当前脚本启动的协同程序

如果你把调用协同程序的游戏对象GameObject调用方法gameObject.SetActive(false) 那么协同程序也会停止。
调用Destroy(example)(其中example是MonoBehaviour实例)立即触发OnDisable, Unity处理协程,有效地停止它。最后,在帧结束时调用OnDestroy。

注意:如果你通过设置enabled为false来禁用MonoBehaviour, Unity不会停止协程。

协程的执行方式不同于其他脚本代码。Unity中的大多数脚本代码出现在单个位置的性能跟踪中,在特定的回调调用之下。然而,协程的CPU代码总是出现在跟踪中的两个位置。

每当Unity启动协程时,协程中的所有初始代码,从协程方法开始直到第一个yield语句,都会出现在跟踪中。初始代码通常在调用StartCoroutine方法时出现。 Unity回调生成的协程(比如返回IEnumerator的Start回调)首先出现在它们各自的Unity回调中。

协程代码的其余部分(从第一次恢复到完成执行)出现在Unity主循环中的DelayedCallManager行中。

这是因为Unity执行协同程序的方式。
C#编译器会自动生成支持协程的类的实例。
Unity然后使用这个对象来跟踪跨多个调用单个方法的协程的状态。

因为协同程序中的局部作用域变量必须在yield调用中持续存在,所以Unity将局部作用域变量提升到生成的类中,在协程期间仍在堆上分配。

该对象还跟踪协程的内部状态:它记住在代码中的哪个点,协程在产生后必须恢复。

因此,协程启动时产生的内存压力等于固定的开销分配加上其局部作用域变量的大小。

启动协程的代码构造并调用一个对象,

然后Unity的DelayedCallManager在协程的yield条件满足时再次调用它。因为协程通常在其他协程之外启动,

这将它们的执行开销划分为yield调用和DelayedCallManager。

你可以使用Unity Profiler来检查和理解Unity在你的应用程序中执行协同程序的位置。要做到这一点,

在启用深度剖析(Deep Profiling enabled)的情况下对应用程序进行剖析,

它可以分析脚本代码的每个部分并记录所有函数调用。

然后,您可以使用CPU Usage Profiler模块来调查应用程序中的协程。

最佳实践是将一系列操作压缩到尽可能少的单个协程。嵌套协程有助于代码的清晰度和维护,但是它们会增加内存开销,因为协程要跟踪对象。

如果协程每一帧都运行,并且不会对长时间运行的操作产生影响,用Update或LateUpdate回调代替它会更高效。如果您有长时间运行或无限循环的协程,这很有用。

Unity 协同程序是通过迭代器实现的

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值