一、协程的概述:

1.定义:

官方的定义是:

A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.

即协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。

Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足),但也有些特例。

2.作用:

使用协程的作用一共有两点:

1)延时(等待)一段时间执行代码;

2)等某个操作完成之后再执行后面的代码——通常用于控制运动,序列,以及对象的行为(充当状态机)

总结起来就是一句话:控制代码在特定的时机执行。

注:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。经过测试验证,协程至少是每帧的LateUpdate()后去运行。

以下就是MonoBehaviour函数的执行流程图:

二、使用方式:

其实协程最简单的调用就是用StartCoroutine去开始一个协程处理函数,这个函数可以是带参也可是无参的,但是其返回值类型必须是IEnumerator类型的。

using UnityEngine;
using System.Collections;public class NewBehaviourScript : MonoBehaviour {public Transform fish;// Use this for initializationvoid Start () {StartCoroutine(Move(100));}IEnumerator Move(int speed){float ut = 0;while (true){ut += Time.deltaTime;if (ut >= 2){ut = 0;var angles = fish.transform.localEulerAngles;angles.z += 180;fish.transform.localEulerAngles = angles;}fish.Translate(speed * Time.deltaTime, 0, 0);yield return 0;}}}

协程允许你的代码执行到一半的时候,先去做完别的事情再回来,这样处理许多事情就会变得简单。比如做俄罗斯方块的时候,但算法满足消去一行的时候,你需要播放一个动画,再消去,通常的做法就是把游戏状态转到动画状态,这样才能保证消去的时候暂停其他动作,比如继续下落方块。如果用协程就直接把权限交给协程就ok。

1.yield语句:

是一个特殊的返回类型,它确保函数从yield语句的下一行继续执行。

while(true) {  // 做步骤0  yield return 0;  // 等待一帧  // 做步骤1  yield return 2;  // 等待两帧  // ...
}

你也可以传递时间值到yield语句,Update函数会在yield结束后执行下一语句:

// do something  yield return WaitForSeconds  (5.0);  //等待5秒  // do something more...

不能在Update或FixedUpdate函数内使用yield,但是你能使用 StartCoroutine开始一个函数。yield return可以看做是一种特殊的return,会返回到父类继续执行,但是yield return后面的类型或方法会有一个执行条件,当条件满足时会回调包含yield的子函数。例如:

void Start () {  print("Starting:" + Time.time);  StartCoroutine(WaitAnPrint(2.0F));  print("Before WaiAndPrint:" + Time.time);  }
IEnumerator WaitAndPrint(float waitTime)  {  yield return new WaitForSeconds(waitTime);  print("WaitAndPrint:" + Time.time);      }

在执行yield return new WaitForSeconds(waitTime)时暂停的条件没有满足,故返回到start函数中继续执行,直到满足条件后再回调WaitAndPrint,所以输出为:

Starting:0
Before WaiAndPrint:0
WaitAndPrint:2.12291

但是,假如yield return语句所在的函数已经是顶级函数,即不存在父类,则此时程序会堵塞等待yield return条件满足之后再继续执行语句之后的内容。

2.IEnumerator(迭代器):

协程其实就是一个Enumerator(迭代器),IEnumerator接口有两个方法:Current和MoveNext(),只有当MoveNext()返回true时才可以访问Current,否则会报错。迭代器方法运行到yield return语句时,会返回一个expression表达式并保留当前在代码中的位置。当下次调用迭代器函数时,执行从该位置重新启动。

Unity在每帧所做的工作就是:调用协程(迭代器)的MoveNext方法,如果返回true,就从当前位置继续往下执行。

Hijack

这里在介绍一个协程的交叉调用类 Hijack(参见附件):

C#代码  
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. using System.Collections;
  6. [RequireComponent(typeof(GUIText))]
  7. public class Hijack : MonoBehaviour {
  8. //This will hold the counting up coroutine
  9. IEnumerator _countUp;
  10. //This will hold the counting down coroutine
  11. IEnumerator _countDown;
  12. //This is the coroutine we are currently
  13. //hijacking
  14. IEnumerator _current;
  15. //A value that will be updated by the coroutine
  16. //that is currently running
  17. int value = 0;
  18. void Start()
  19. {
  20. //Create our count up coroutine
  21. _countUp = CountUp();
  22. //Create our count down coroutine
  23. _countDown = CountDown();
  24. //Start our own coroutine for the hijack
  25. StartCoroutine(DoHijack());
  26. }
  27. void Update()
  28. {
  29. //Show the current value on the screen
  30. guiText.text = value.ToString();
  31. }
  32. void OnGUI()
  33. {
  34. //Switch between the different functions
  35. if(GUILayout.Button("Switch functions"))
  36. {
  37. if(_current == _countUp)
  38. _current = _countDown;
  39. else
  40. _current = _countUp;
  41. }
  42. }
  43. IEnumerator DoHijack()
  44. {
  45. while(true)
  46. {
  47. //Check if we have a current coroutine and MoveNext on it if we do
  48. if(_current != null && _current.MoveNext())
  49. {
  50. //Return whatever the coroutine yielded, so we will yield the
  51. //same thing
  52. yield return _current.Current;
  53. }
  54. else
  55. //Otherwise wait for the next frame
  56. yield return null;
  57. }
  58. }
  59. IEnumerator CountUp()
  60. {
  61. //We have a local increment so the routines
  62. //get independently faster depending on how
  63. //long they have been active
  64. float increment = 0;
  65. while(true)
  66. {
  67. //Exit if the Q button is pressed
  68. if(Input.GetKey(KeyCode.Q))
  69. break;
  70. increment+=Time.deltaTime;
  71. value += Mathf.RoundToInt(increment);
  72. yield return null;
  73. }
  74. }
  75. IEnumerator CountDown()
  76. {
  77. float increment = 0f;
  78. while(true)
  79. {
  80. if(Input.GetKey(KeyCode.Q))
  81. break;
  82. increment+=Time.deltaTime;
  83. value -= Mathf.RoundToInt(increment);
  84. //This coroutine returns a yield instruction
  85. yield return new WaitForSeconds(0.1f);
  86. }
  87. }
  88. }

上面的代码实现是两个协程交替调用。

3.协程暂停:

由于协程与MonoBehaviour一样,也是绑在对象Object上的,所以协程的运行和停止与绑定对象的active有关,MonoBehaviour.enabled = false 协程会照常运行,但 gameObject.SetActive(false) 后协程却全部停止,即使在Inspector把  gameObject 激活还是没有继续执行。

也就是说:协程虽是在MonoBehaviour启动的(使用StartCoroutine启动),但是协程函数的地位完全跟MonoBehaviour是同一个层次的,不受MonoBehaviour的状态影响,但是跟MonoBehaviour一样受到绑定的gameObject控制,也应该是和MonoBehaviour脚本一样每帧“轮询”yield的条件是否满足。

Unity3D 学习笔记6 ——协程相关推荐

  1. lua学习笔记之协程

    1.基础 所有协程相关的在表coroutine中,创建通过create来创建,参数为函数,返回值类型为thread. 协程状态有:suspended, running, normal, dead.通过 ...

  2. Go语言学习笔记 - PART13 - 协程与通道

    协程 应用程序处理并发的部分 协程跟操作系统的线程之间不是一对一的关系 协程是独立执行的,它们之间必须同行才会变得有用 通道 负责协程之间的通信,从而避免所有由共享内存导致的陷阱 通道只能传输一种类型 ...

  3. Kotlin学习笔记26 协程part6 协程与线程的关系 Dispatchers.Unconfined 协程调试 协程上下文切换 Job详解 父子协程的关系

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 协程与线程的关系 import kotlinx.coroutines.* import java.util.concu ...

  4. Kotlin学习笔记25 协程part5 协程的同步与异步

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 程序运行时间统计measureTimeMillis /*** 程序运行时间统计measureTimeMillis** ...

  5. Kotlin学习笔记24 协程part4 协程的取消与超时

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 如何取消协程 import kotlinx.coroutines.*/*** 协程的取消*/fun main() = ...

  6. Kotlin学习笔记23 协程part3 lambda表达式深入 挂起函数 全局协程

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 lambda表达式深入 /*** lambda 表达式深入* 当函数参数是函数时 并且该函数只有一个参数 可以不传入任 ...

  7. Kotlin学习笔记22 协程part2 join CoroutineScope 协程vs线程

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 1 Job的join方法 import kotlinx.coroutines.* /*** Job的join方法* 它会挂 ...

  8. Kotlin学习笔记21 协程part1 基本概念

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 本节先介绍协程的相关概念 概念可能枯燥,我们先要了解协程中的相关概念 然后结合代码理解这些概念 加深印象 协程的定义 协程 ...

  9. Unity3D 学习笔记4 —— UGUI+uLua游戏框架

    Unity3D 学习笔记4 -- UGUI+uLua游戏框架 使用到的资料下载地址以及基础知识 框架讲解 拓展热更过程 在这里我们使用的是uLua/cstolua技术空间所以提供的UGUI+uLua的 ...

最新文章

  1. Lua基本语法-书写规范以及自带常用函数
  2. Oracle ADG vs DG
  3. Visual C++ MFC/ATL开发-高级篇(一)
  4. html太极图代码静态_如何做URL静态化?和页面的静态化
  5. lamp架构之升级php版本
  6. Imageloader4-ImageLoader中的变量
  7. Couchbase应用示例(初探)
  8. Linux Centos7 以及window 10 Kali2020.4 安装 nvm node npm cnpm yarn
  9. Maven的pom.xml文件详解------Environment Settings
  10. 《Python Cookbook 3rd》笔记(5.13):读写压缩文件
  11. python入门之函数调用-python之函数入门
  12. C++中BEGIN_MSG_MAP或者BEGIN_MESSAGE_MAP的个人理解
  13. SpringBoot中快速实现邮箱发送
  14. R包制作(千字详细图文)
  15. eclipse安装SVN插件(2020最新,亲测可用)
  16. 快递行业总结(四)快递公司当前特征
  17. 基于arduino uno + WS2812b RGB灯带单一颜色跑马灯控制程序
  18. 谐波小波 matlab,基于谐波小波的电力系统谐波分析
  19. 现代控制理论(5)——线性定常系统的综合
  20. spark算子--action篇

热门文章

  1. 手工皮具的大坑之路-封边感悟与工具
  2. 《MLB棒球创造营》:走近棒球运动·德州游骑兵队
  3. WordPress博客系统搜索引擎优化seo全攻略-新华站长网
  4. 月影无终的伤感日志最新推荐:搁浅的回忆,谁来承受
  5. 盘点各大互联网公司2017中秋月饼设计,你最喜欢哪一个?
  6. matlab怎么绘制零极点,matlab中画系统零极点的方法
  7. 造车失败后投身机器人和AI,我笑戴森太疯癫,戴森笑我看不穿
  8. 牛客NC272 栈的压入、弹出序列
  9. 浅析webpack的原理
  10. 微服务-高并发-思路