文章目录
- 什么是协程
- 协程的应用 - IEnumerator
- 如何控制协程的暂停
- 协程的另一种写法 - Invoke
- 场景管理
多看代码块中的注释
什么是协程
A coroutine alows vou to spreacwhere it left off on the following anc return control toolinencoeframe.
协程允许您将任务分布在多个帧中。在 Unity 中,协程是一种方法可以暂停执行并将控制权返回给 Unity,然后在下一帧中从中断的地方继续。
In most situations,whenyou cal a method,it runs to completion and then retumns control to the caling method,plus any optional return vales.This means that any acion that takes place within amethod must happen within a single frame update.
在大多数情况下,调用方法时,它会运行到完成,然后将控制权返回给调用方法以及任何可选的返回值。这意味着在方法中发生的任何操作都必须在单个帧更新中发生。
n situations where you would like to use a method cal to contain a procedural animation or a seauence of events over time. you can use a coroutine如果希望使用方法调用来包含过程动画或随时间推移的事件序列,则可以使用协程
Howelver its imporeant to rememoerthat coroutines arent threads. Sinchronous operations thatrun within a coroutine stlexecute on the main thread.f you wantorecucethe amountof CpUtimespent on the mainthread,i’sjust as important to avoid blocking perations in coroutines as in any other secript code.fyouwanto use mult;threaded codewithin Uhiy, consider the C# Job System.但是,重要的是要记住协程不是线程。在协程中运行的同步操作仍在主线程上执行。如果要减少在主线程上花费的 CPU 时间,避免在协程中阳塞操作与在任何其他脚本代码中一样重要。如果要在 uniy 中使用多线程代码,请考虑 C# 作业系统,
It’s best to use coroutines if you need to deal with long asynchronous operations, such as waiing for HTTP transfers, asset loads,or file l/0 to complete如果需要处理长时间的异步操作,例如等待 HTTP 传输、资产加载或文件 V/0 完成,最好使用协程。
简单来说
协程,即为协同程序. Unity中的协程由协程函数和协程调度器两部分构成.协程函数使用的是C#的迭代器, 协程调度器则利用了MonoBehaviour中的生命周期函数来实现. 协程函数实现了分步, 协程调度器实现了分时.
注:因为协程分时分步执行的特性,当多个协程的耗时操作挤在同一时间执行也会造成卡顿。
协程的应用 - IEnumerator
我们先建立一个cube和一段C#代码一起放进新创建的空件 EP18_01_Delay call 中
代码具体如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EP18_01_Delaycall : MonoBehaviour
{
public GameObject cubeGo;
// Start is called before the first frame update
void Start()
{
StartCoroutine(DelayInstantiate());//正常方法是不会等协程的
}
// Update is called once per frame
void Update()
{
}
//
IEnumerator DelayInstantiate()
{
Debug.Log("协程开始了");
yield return new WaitForSeconds(3);//等待3秒
//yield return 0;//数字0没有意义,多少都一样,都是等待一帧
Instantiate(cubeGo);
}
}
在保存好源码后运行,等待3秒后就会在场景中原位置克隆一个新的cube
但是这样是不是还没法理解协程的意义
我们先利用一个新的方法名Test,验证一般情况下整个场景的运行情况
void Start()
{
Debug.Log(1);
Debug.Log(2);
Test(0);//调用方法
}
private void Test(int num)
{
Debug.Log(num);
}
它几乎第一时间就把这些信息都打印出来了
但是换成协程试试
void Start()
{
StartCoroutine(DelayInstantiate());//正常方法是不会等协程的
Debug.Log(1);
Debug.Log(2);
Test(0);//调用方法
}
IEnumerator DelayInstantiate()
{
Debug.Log("协程开始了");
yield return new WaitForSeconds(3);//等待3秒
//yield return 0;//数字没有意义,都是等待一帧
Debug.Log(3);
Instantiate(cubeGo);
}
明明是最早的语句,但是3秒后才打印出来(这里差4秒是因为毫秒的存在)
这样看过来,我们还能意识到一点,就是在携程运行时,主程序不会等待写成程序运行完后在进行,而是一起进行,所以我们可以看成写成程序是一种分支运行方法,不影响主程序进程
如何控制协程的暂停
有的时候,我们在游戏过程中,可能因为一些原因需要停止并关闭掉一些正在运行流程,这种方法在C#中有两种常用的方法
第一种方法是用方法名的字符串形式,在Updata里通过某种操作(笔者通过按下S键)来停止
void Start()
{
StartCoroutine("DelayInstantiate");//以字符串名的形式运行
Debug.Log(1);
Debug.Log(2);
Test(0);//调用方法
}
void Update()
{
if(Input.GetKeyDown(KeyCode.S))//按下S时
{
//StopAllCoroutines();//停止整个协程
StopCoroutine("DelayInstantiate");
}
}
第二种方法不用协程方法名的字符串,直接用其本身,不同的是需要定义一个程序内部的全局变量,通过这个变量作为媒介联系协程
private Coroutine coroutine;//内部全局变量
void Start()
{
coroutine = StartCoroutine(DelayInstantiate());
Debug.Log(1);
Debug.Log(2);
Test(0);//调用方法
}
void Update()
{
if(Input.GetKeyDown(KeyCode.S))//按下S时
{
//StopAllCoroutines();//停止整个协程
StopCoroutine(coroutine);
}
}
协程的另一种写法 - Invoke
这里我们新创建一个方法用来克隆立方体,然后利用Invoke来进行协程
void Start()
{
Invoke("InstantantiateCube",3);//3意味着3秒后运行方法
}
private void InstantantiateCube()
{
Instantiate(cubeGo);
}
最后也是成功克隆(这里将克隆后的立方体进行了移动)
还有一种循环协程的方法
InvokeRepeating("InstantantiateCube", 3, 1);//1表示等3秒后第一个立方体克隆完后,每1秒后在克隆一次)
成功克隆(为了看得清楚,笔者进行了移动)
场景管理
我们先创建两个新的场景 test1 和 test2,并在 test2 里创建一个立方体
然后调出右上角的场景管理器
全部添加进去
重新编辑一块代码如下,我们会发现在1秒后将原来默认的SampleScene场景变成了1号场景test1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;//场景的命名空间
public class EP18_02_ : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Invoke("LoadScene",2);
}
// Update is called once per frame
void Update()
{
}
private void LoadScene()
{
SceneManager.LoadScene(1);//加载第1个场景
}
}
同理也是可以进行别的场景替换