Unity协程简述(简单用法,简易分析)
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协程简述(简单用法,简易分析)相关推荐
- Unity协程的简单应用
Unity协程是一种特殊的函数,可以让你在Unity中创建一种类似于多线程的异步操作.它可以在需要等待某个操作完成时,暂停执行当前代码,等待某个条件满足后再继续执行. 在一般情况下 unity中调用函 ...
- Unity 协程用法总结
Unity 协程用法总结 协程:协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行,注意协程不是线程,只是具有线程特点的"伪协程". 协程的使用需要直接或间接 ...
- unity怪物攻击玩家减血_利用Unity协程实现一个简单的怪物寻路与跟随AI
利用Unity协程实现一个简单的怪物寻路与跟随AI,通过分析怪物行为与逻辑,实现简单的平面怪物寻路与跟随效果. 分析 对于游戏中怪物的行为,简单归纳为如下几部分: 怪物在预设范围内随机移动. 玩家走入 ...
- Unity协程实现分析以及Lua协程与Unity协程的混合使用
1,节选翻译一篇stackoverflow关于Unity协程实现的讨论 - The big clues are in the C# version. Firstly, note that the re ...
- unity 协程特点
协程 一. Unity中使用协程 1. 什么是协程 2. 如何使用 3. 协程的应用场景 创建补间动画 打字机效果 异步加载资源 4. 注意事项 二. Unity协程的底层原理 1. 协程本体:C#的 ...
- Unity协程深入及实现
Unity协程深入 文章目录 Unity协程深入 Unity协程简介 Unity协程的用法 协程的原理 协程的缺陷 自己实现协程 参考 Unity协程简介 Unity中的协程能够允许我们将一个任务分散 ...
- 【Unity】Unity协程(Coroutine)的原理与应用
文章目录 前言 一.什么是协程 二.应用场景 1.异步加载资源 2.将一个复杂程序分帧执行 3.定时器 三.协程的使用 注意事项 四.Unity协程的底层原理 1. 协程本体:C#的迭代器函数 2. ...
- Unity 协程深入解析与原理
先来直接放一段代码 1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 p ...
- Unity协程(Coroutine)原理深入剖析
Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 记得去年6月份刚开始实习的时候,当时要我写网 ...
最新文章
- node 获取表单数据 为空_数据结构与算法(python)单向循环链表
- 京东11·11:撬动数据中心的支点——京东阿基米德
- 常考数据结构与算法:删除链表的倒数第n个节点
- windows 7 旗舰版下无法安装 msi 文件 解决办法
- 【转】jQuery.ajax向后台传递数组问题
- 数据库设计:pd工程创建数据库表
- MySQL JDBC驱动程序如何处理准备好的语句
- SAP License:赛锐信息访谈启示录(五)
- Android应用程序启动Binder线程源码分析
- Tensorflow2.0:使用Keras自定义网络实战
- 天气预报 API 各城市编码
- python做var模型_VAR模型学习笔记
- IT业台风警报(一)——望天
- ctrl键失灵的问题-记kali 1.0升级kali 2.0
- 制造业质量管理四大病因
- 安卓手机版微信聊天加密软件 悬浮窗版本
- 一心多用多线程-future-java线程中的异步执行
- 【Bilibili视频嵌入技巧】如何嵌入720PBilibili视频
- 什么是职业规划?如何进行职业规划?
- brain怎么读_Brian这个英文怎么读是什么意思??