Unity协程

  • 协程的简单用法
    • 简述
    • 函数
  • 协程的执行顺序
  • 协程替我们做了什么
    • Yleid Return
    • 如何进行跳帧,延迟,等待的操作
      • 从IL的角度分析
      • IL语言
      • 总结
  • 自定义一个迭代器
  • 优化
  • 最后

协程的简单用法

简述

  • 协程实际上就是一个迭代器(IEnumerator),内部可以定义一些Yield Return挂起函数,判断协程函数内部执行的逻辑(通过Yield Return 延时,跳帧,等待下载或加载等操作将函数进行分步操作)
  • 协程是基于生命周期的(下面会将执行顺序),是同步的,不是异步的(同一时间只会执行一个协程)
  • 协程函数会被编译成一个状态机,每个状态都会关联下一个状态直至下一个为空

函数

  • 按常用排序
  • WaitForSeconds
  • WaitForEndOfFrame
  • WaitForFixedUpdate
  • WaitForSecondsRealtime
  • WaitUnil
  • WaitWhile
//WaitUntil:当返回值为True时则继续执行后面的内容
StartCoroutine(WaitUntilFunc());
//WaitForSeconds:延迟几秒后执行,时间单位为Float
StartCoroutine(WaitForSecondsFunc());
//WaitForSeconds:延迟几秒后执行(不受缩放影响),时间单位为Float
StartCoroutine(WaitForSecondsRealtimeFunc());
//WaitWhile:当返回值为False时则继续执行后面的内容
StartCoroutine(WaitWhileFunc());
//WaitForEndOfFrame:当前帧结束前调用
StartCoroutine(WaitForEndOfFrameFunc());
//当开启的协程结束时调用
StartCoroutine(WaitForStartCoroutine());IEnumerator WaitUntilFunc()
{Debug.Log("WaitUntilFunc Start~");//10帧后执行yield return new WaitUntil(() => _frame> 10);Debug.Log("WaitUntilFunc End~");
}
IEnumerator WaitForSecondsFunc()
{Debug.Log("WaitForSecondsFunc Start~");//延迟两秒后执行(时间受缩放影响)yield return new WaitForSeconds(2f);Debug.Log("WaitForSecondsFunc End~");
}IEnumerator WaitForSecondsRealtimeFunc()
{Debug.Log("WaitForSecondsRealtimeFuncStart~");//延迟两秒后执行(时间不受缩放影响)yield return new WaitForSecondsRealtime(2f);Debug.Log("WaitForSecondsRealtimeFuncEnd~");
}IEnumerator WaitWhileFunc()
{Debug.Log("WaitWhileFunc Start~");//10帧后执行yield return new WaitWhile(()=> !(_frame>10));Debug.Log("WaitWhileFunc End~");
}IEnumerator WaitForEndOfFrameFunc()
{Debug.Log("WaitForEndOfFrameFunc Start~");//当前帧结束前后调用yield return new WaitForEndOfFrame();Debug.Log("WaitForEndOfFrameFunc End~");
}IEnumerator WaitForAsyncOperation()
{Debug.Log("WaitForAsyncOperation Start~");//当场景加载完毕后调用AsyncOperation async = null; //异步加载完场景时完后调用async = SceneManager.LoadSceneAsync("SampleScene");//异步加载完AB时调用async = AssetBundle.LoadFromFileAsync("Path");//异步加载完资源时调用async = Resources.LoadAsync("Path");//异步下载完后执行var request = UnityWebRequest.Get(new Uri("www.baidu.com"));async = request.SendWebRequest();yield return async;Debug.Log("WaitForAsyncOperation End~");
}IEnumerator WaitForStartCoroutine()
{Debug.Log("WaitForStartCoroutine Start~");//当开启的协程结束时调用(不是被挂起时噢,而是完成了协程)yield return StartCoroutine(WaitForSecondsFunc());Debug.Log("WaitForStartCoroutine End~");
}

协程的执行顺序

官网更加详细点这里哟
执行顺序

  • FixedUpdate
  • yield WaitForFixedUpdate
  • Update
  • yield null
  • yield WaitForSeconds
  • yield WWW
  • yield StartCoroutine
  • LateUpdate
  • yield WaitForEndOfFrame

协程替我们做了什么

Yleid Return

简述
yield return 用来判断协程内部是否挂起或继续执行的语句

支持类型

  • IEnumerator: 通过MoveNext函数来判断是否继续挂起函数
  • CustomYieldInstruction:内部通过判断keepWaiting的值来判断是否继续执行协程,true为挂起,false为继续执行
  • YieldInstruction:Coroutine就是继承该对象,当yield return Coroutine时就是等待开启的协程执行结束再往后执行
  • AsyncOperation:等待加载完毕再往后执行
  • Coroutine:开启协程的返回值,仅仅引来引用协程,不具有任何暴露的属性和函数(当yield return coroutine时,当Coroutine为Null时协程继续往后执行)

yield return做了什么操作
可以看到yield return 修改了 current 与 state的值。而current 表示当前迭代对象的值,而state在执行代码前会被修改一次(表示正在执行),而代码执行完毕后会被修改一次(表示下一个迭代器的标识)

执行前修改了 state值(表示正在执行)
IL_0025: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'// [95 5 - 95 6]
IL_002a: nop// [96 9 - 96 51]
IL_002b: ldstr        "WaitForEndOfFrameFunc Start~"
IL_0030: call         void [UnityEngine.CoreModule]UnityEngine.Debug::Log(object)
IL_0035: nop// [98 9 - 98 46]
IL_0036: ldarg.0      // this
IL_0037: newobj       instance void [UnityEngine.CoreModule]UnityEngine.WaitForEndOfFrame::.ctor()
修改了当前current对象
IL_003c: stfld        object TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>2__current'
IL_0041: ldarg.0      // this
IL_0042: ldc.i4.1
执行完 debug.log 后再次修改state值表示结束以及能继续迭代下一个对象
IL_0043: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'

如何进行跳帧,延迟,等待的操作

从IL的角度分析

当协程内部只定义了一个yield return挂起函数时,不会直接编译成switch,而是成编译成类似if else语句,而当出现两个yield return或以上时就会编译成一个switch语句将代码分割。

IL语言

--C#--
IEnumerator WaitForEndOfFrameFunc()
{Debug.Log("WaitForEndOfFrameFunc Start~");//当前帧结束前后调用yield return new WaitForEndOfFrame();Debug.Log("WaitForEndOfFrameFunc End~");yield return new WaitForEndOfFrame();Debug.Log("WaitForEndOfFrameFunc End1~");}--IL部分--
.method private final hidebysig virtual newslot instance boolMoveNext() cil managed
{.override method instance bool [netstandard]System.Collections.IEnumerator::MoveNext().maxstack 2.locals init ([0] int32 V_0)IL_0000: ldarg.0      // thisIL_0001: ldfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'IL_0006: stloc.0      // V_0IL_0007: ldloc.0      // V_0IL_0008: switch       (IL_001b, IL_001d, IL_001f)IL_0019: br.s         IL_0021IL_001b: br.s         IL_0023IL_001d: br.s         IL_004aIL_001f: br.s         IL_0070IL_0021: ldc.i4.0IL_0022: retIL_0023: ldarg.0      // thisIL_0024: ldc.i4.m1IL_0025: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'// [95 5 - 95 6]IL_002a: nop// [96 9 - 96 51]IL_002b: ldstr        "WaitForEndOfFrameFunc Start~"IL_0030: call         void [UnityEngine.CoreModule]UnityEngine.Debug::Log(object)IL_0035: nop// [98 9 - 98 46]IL_0036: ldarg.0      // thisIL_0037: newobj       instance void [UnityEngine.CoreModule]UnityEngine.WaitForEndOfFrame::.ctor()IL_003c: stfld        object TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>2__current'IL_0041: ldarg.0      // thisIL_0042: ldc.i4.1IL_0043: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'IL_0048: ldc.i4.1IL_0049: retIL_004a: ldarg.0      // thisIL_004b: ldc.i4.m1IL_004c: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'// [99 9 - 99 49]IL_0051: ldstr        "WaitForEndOfFrameFunc End~"IL_0056: call         void [UnityEngine.CoreModule]UnityEngine.Debug::Log(object)IL_005b: nop// [100 9 - 100 46]IL_005c: ldarg.0      // thisIL_005d: newobj       instance void [UnityEngine.CoreModule]UnityEngine.WaitForEndOfFrame::.ctor()IL_0062: stfld        object TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>2__current'IL_0067: ldarg.0      // thisIL_0068: ldc.i4.2IL_0069: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'IL_006e: ldc.i4.1IL_006f: retIL_0070: ldarg.0      // thisIL_0071: ldc.i4.m1IL_0072: stfld        int32 TestCor/'<WaitForEndOfFrameFunc>d__12'::'<>1__state'// [101 9 - 101 50]IL_0077: ldstr        "WaitForEndOfFrameFunc End1~"IL_007c: call         void [UnityEngine.CoreModule]UnityEngine.Debug::Log(object)IL_0081: nop// [102 5 - 102 6]IL_0082: ldc.i4.0IL_0083: ret} // end of method '<WaitForEndOfFrameFunc>d__12'::MoveNext

总结

这篇文章很好的分析了IL和C#部分
这篇文章很好的解释了跳帧,延迟,等待的操作
看IL后发现,实际上协程函数最终会被MoveNext函数调用。在switch开始之前先去取 “<>1__state” 的值,然后再去进行switch进行判断,当switch找到对应的case时并执行完了代码块就会修改 “<>1__state” 的值以及 “<>2__current”,然后结束语句,直到Unity生命周期再次调用该协程,并继续进行switch直至 “<>1__state” 的值代表为null。

根据上述文章的分析,跳帧与延迟等操作应该是在MoveNext函数内部做了特殊操作,当Current对象为延迟或跳帧对象时,会进行额外的处理

自定义一个迭代器

就不一一展示用法了,代码太长,需要自己去尝试

    private void Start(){StartCoroutine(TestCor());}IEnumerator TestCor(){Debug.Log("TestCor Start~");yield return new CustomIEnumerator();yield return new Custom2IEnumerator();yield return new Custom3IEnumerator();yield return new Custom4IEnumerator();Debug.Log("TestCor End~");}
}
//继承了这四个对象则可以被yield reutrn 这样可以自定义一些挂起函数
public class CustomIEnumerator : IEnumerator
{private int _index = -1;private string[] strName = {"Alan", "Kino", "Bruce"};/// <summary>/// 每帧被调用/// </summary>/// <returns></returns>public bool MoveNext(){_index++;Debug.Log("MoveNext:" + (_index > strName.Length).ToString());return false;return _index < strName.Length;}public void Reset(){Debug.Log("Reset");_index = 0;}public object Current {get{Debug.Log("获取当前对象:" + strName[_index]);return strName[_index];}}
}
public class Custom2IEnumerator : CustomYieldInstruction
{public override bool keepWaiting { get; }
}
public class Custom3IEnumerator : YieldInstruction
{}
public class Custom4IEnumerator : AsyncOperation
{}

优化

  • 有些不需要每帧进行执行的任务可以使用协程延迟
    官网案例
IEnumerator DoCheck()
{//循环调用for(;;) {ProximityCheck();//延迟0.1s后执行yield return new WaitForSeconds(.1f);}
}
  • 缓存挂机函数
private WaitForSeconds delay = new WaitForSeconds(.1f);
IEnumerator TestCor()
{//循环调用for(;;) {//延迟0.1s后执行yield return delay;}
}

最后

简而言之,越深入学越知道自己的浅薄,希望各位同学能从本章中学到什么。也希望各位同学给予一些更好的观点~

Unity协程简述(简单用法,简易分析)相关推荐

  1. Unity协程的简单应用

    Unity协程是一种特殊的函数,可以让你在Unity中创建一种类似于多线程的异步操作.它可以在需要等待某个操作完成时,暂停执行当前代码,等待某个条件满足后再继续执行. 在一般情况下 unity中调用函 ...

  2. Unity 协程用法总结

    Unity 协程用法总结 协程:协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行,注意协程不是线程,只是具有线程特点的"伪协程". 协程的使用需要直接或间接 ...

  3. unity怪物攻击玩家减血_利用Unity协程实现一个简单的怪物寻路与跟随AI

    利用Unity协程实现一个简单的怪物寻路与跟随AI,通过分析怪物行为与逻辑,实现简单的平面怪物寻路与跟随效果. 分析 对于游戏中怪物的行为,简单归纳为如下几部分: 怪物在预设范围内随机移动. 玩家走入 ...

  4. Unity协程实现分析以及Lua协程与Unity协程的混合使用

    1,节选翻译一篇stackoverflow关于Unity协程实现的讨论 - The big clues are in the C# version. Firstly, note that the re ...

  5. unity 协程特点

    协程 一. Unity中使用协程 1. 什么是协程 2. 如何使用 3. 协程的应用场景 创建补间动画 打字机效果 异步加载资源 4. 注意事项 二. Unity协程的底层原理 1. 协程本体:C#的 ...

  6. Unity协程深入及实现

    Unity协程深入 文章目录 Unity协程深入 Unity协程简介 Unity协程的用法 协程的原理 协程的缺陷 自己实现协程 参考 Unity协程简介 Unity中的协程能够允许我们将一个任务分散 ...

  7. 【Unity】Unity协程(Coroutine)的原理与应用

    文章目录 前言 一.什么是协程 二.应用场景 1.异步加载资源 2.将一个复杂程序分帧执行 3.定时器 三.协程的使用 注意事项 四.Unity协程的底层原理 1. 协程本体:C#的迭代器函数 2. ...

  8. Unity 协程深入解析与原理

    先来直接放一段代码 1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 p ...

  9. Unity协程(Coroutine)原理深入剖析

    Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 记得去年6月份刚开始实习的时候,当时要我写网 ...

最新文章

  1. node 获取表单数据 为空_数据结构与算法(python)单向循环链表
  2. 京东11·11:撬动数据中心的支点——京东阿基米德
  3. 常考数据结构与算法:删除链表的倒数第n个节点
  4. windows 7 旗舰版下无法安装 msi 文件 解决办法
  5. 【转】jQuery.ajax向后台传递数组问题
  6. 数据库设计:pd工程创建数据库表
  7. MySQL JDBC驱动程序如何处理准备好的语句
  8. SAP License:赛锐信息访谈启示录(五)
  9. Android应用程序启动Binder线程源码分析
  10. Tensorflow2.0:使用Keras自定义网络实战
  11. 天气预报 API 各城市编码
  12. python做var模型_VAR模型学习笔记
  13. IT业台风警报(一)——望天
  14. ctrl键失灵的问题-记kali 1.0升级kali 2.0
  15. 制造业质量管理四大病因
  16. 安卓手机版微信聊天加密软件 悬浮窗版本
  17. 一心多用多线程-future-java线程中的异步执行
  18. 【Bilibili视频嵌入技巧】如何嵌入720PBilibili视频
  19. 什么是职业规划?如何进行职业规划?
  20. brain怎么读_Brian这个英文怎么读是什么意思??

热门文章

  1. 信息化那点事之:一把手工程
  2. Ceph存储引擎BlueStore简析
  3. 18-考虑柔性负荷的综合能源系统低碳经济优化调度MATLAB程序
  4. vivos7和荣耀x10哪个好 vivos7和荣耀x10区别评测
  5. 【数据库课设】图书馆资源管理系统 源码+流程图+结构设计(借还图书 逾期罚款 图书管理 读者管理 信息查询)python实现
  6. 编写复用组件card
  7. 网络营销策划:揭秘企业营销策划方案三大法则
  8. MySQL#复制 双Yes的假死故障造成主从不一致
  9. 天使湖北武汉汇美是骗子C
  10. mysql常见字符集_MySQL字符集