Unity–协程–Coroutine
1. 协程的基本概念
-
基本概念:不是线程,将代码按照划分的时间来执行,这个时间可以是具体的多少秒,也可以是物理帧的时间,也可以是一帧的绘制结束的时间。
-
协程的写法:通过返回
IEnumerator
的函数实现,使用yield return
语句暂停执行。 -
等待时间:使用
WaitForSeconds
等待特定时间,在Update和LateUpdate之间执行 -
其他等待选项:
WaitForEndOfFrame
等待当前帧结束,可以用来截取画面,游戏帧画面分析WaitForFixedUpdate
等待下一个固定物理帧更新时执行。下一帧执行:
yield return 数字
和yield return null
; 在Update和LateUpdate之间执行public IEnumerator MyCoroutine(int i, string str, float t) { print(i); // 返回值IEumerator yield return 来分时函数,即等待多少秒后继续执行函数 yield return new WaitForSeconds(5.0f); // 等待5s后执行 print(str); yield return new WaitForSeconds(3.0f); print(t); yield return new WaitForEndOfFrame(); // 等待当前帧绘制完毕后执行 print(t); yield return new WaitForFixedUpdate(); // 直到下一个固定更新周期到了执行 print(i); // ... }
2. 开启协程
-
开启方法:使用
StartCoroutine
方法。 -
开启方式:可以直接传递方法名或先获取
IEnumerator
对象再传递。// 2.1 不同于文文普通的函数调用来开启协程 // MyCoroutine(3, "不能使用普通方法调用开启/协程", 5.0f); // 2.2 开启方法1 public void StartMyCoroutine1() { StartCoroutine(MyCoroutine(1, "开启协程1", 1.0f)); } // 2.3 开启方法2 public void StartMyCoroutine2() { IEnumerator ie = MyCoroutine(1, "开启协程2", 1.0f); StartCoroutine(ie); } // 2.4 开启多个协程 public void StartMyCoroutine3() { // 开启协程后保留写出对象 c1 = StartCoroutine(MyCoroutine(1, "多个协程1", 1.0f)); c2 = StartCoroutine(MyCoroutine(1, "多个协程2", 1.0f)); c3 = StartCoroutine(MyCoroutine(1, "多个协程3", 1.0f)); }
3. 关闭协程
-
关闭所有协程:使用
StopAllCoroutines
。 -
关闭特定协程:使用
StopCoroutine
,可以传递IEnumerator
对象、协程的引用或方法名。// 1. 关闭所有协程 public void CloseAllCoroutine() { StopAllCoroutines(); } // 2. 关闭指定协程 public void CloseSpecialCoroutine() { StopCoroutine(c1); // StopCoroutine(IEnumerator routine); 通过IEnumerator关闭 // StopCoroutine(Coroutine routine); 通过协程名称关闭 // StopCoroutine(string methodName); 通过协程函数名关闭 }
关闭指定协程的方法有很多,一般不使用协程函数名关闭
StopCoroutine(IEnumerator routine);
通过IEnumerator关闭StopCoroutine(Coroutine routine);
通过协程名称关闭
StopCoroutine(string methodName);
通过协程函数名关闭
4. 提前跳出协程
使用yield break
在满足条件时提前终止执行。
// 提前跳出协程
public void BreakCoroutine()
{
IEnumerator MyNewCoroutine()
{
Debug.Log("开始执行协同程序");
// 执行一些操作
yield return new WaitForSeconds(1.0f);
Debug.Log("协同程序执行了一秒");
// 在这里,我们检查一个条件
if (SomeCondition())
{
Debug.Log("满足条件,提前结束协同程序");
yield break; // 提前终止协同程序
}
// 如果上面的条件不满足,这将不会执行
yield return new WaitForSeconds(1.0f);
Debug.Log("协同程序又执行了一秒");
}
StartCoroutine(MyNewCoroutine());
}
bool SomeCondition()
{
// 这里是条件的逻辑
return true; // 例如,始终返回 true 来终止协同程序
}
5. 特殊情况
协程当组件或游戏对象失效或者失活的时候怎么办???
协程开启后
- 组件和物体销毁,协程不执行
- 物体失活协程不执行,组件失活协程执行
6.建议
- 协程的使用:适用于处理异步操作和分步骤执行任务,如动画、加载资源。
- 性能考虑:比多线程轻量,适合游戏开发中的非阻塞操作。
- 条件检查:在协程中使用条件检查可以灵活控制流程。
- 资源管理:在使用协程加载资源时,注意资源的释放和内存管理。
7.完成测试代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoroutineTest : MonoBehaviour
{
Coroutine c1;
Coroutine c2;
Coroutine c3;
// 协程程序
// 1. 协程的使用
// 1.1 协程的写法
public IEnumerator MyCoroutine(int i, string str, float t)
{
print(i);
// 返回值IEumerator yield return 来分时函数,即等待多少秒后继续执行函数
yield return new WaitForSeconds(5.0f); // 等待5s后执行
print(str);
yield return new WaitForSeconds(3.0f);
print(t);
//yield return new WaitForEndOfFrame(); // 等待当前帧绘制完毕后执行
//print(t);
//yield return new WaitForFixedUpdate(); // 直到下一个固定更新周期到了执行
//print(i);
}
// 2. 开启协程
// 2.1 不同用普通的函数调用来开启协程
// MyCoroutine(3, "不能使用普通方法调用开启/协程", 5.0f);
// 2.2 开启方法1
public void StartMyCoroutine1()
{
StartCoroutine(MyCoroutine(1, "开启协程1", 1.0f));
}
// 2.3 开启方法2
public void StartMyCoroutine2()
{
IEnumerator ie = MyCoroutine(1, "开启协程2", 1.0f);
StartCoroutine(ie);
}
// 2.4 开启多个协程
public void StartMyCoroutine3()
{
// 开启协程后保留写出对象
c1 = StartCoroutine(MyCoroutine(1, "多个协程1", 1.0f));
c2 = StartCoroutine(MyCoroutine(1, "多个协程2", 1.0f));
c3 = StartCoroutine(MyCoroutine(1, "多个协程3", 1.0f));
}
// 3. 关闭协程
public void CloseAllCoroutine()
{
StopAllCoroutines();
}
// 4. 关闭指定协程
public void CloseSpecialCoroutine()
{
StopCoroutine(c1);
StopCoroutine(c2);
StopCoroutine(c3);
// StopCoroutine(IEnumerator routine); 通过IEnumerator关闭
// StopCoroutine(Coroutine routine); 通过协程名称关闭
// StopCoroutine(string methodName); 通过协程函数名关闭
}
// 5. 提前跳出协程
public void BreakCoroutine()
{
IEnumerator MyNewCoroutine()
{
Debug.Log("开始执行协同程序");
// 执行一些操作
yield return new WaitForSeconds(1.0f);
Debug.Log("协同程序执行了一秒");
// 在这里,我们检查一个条件
if (SomeCondition())
{
Debug.Log("满足条件,提前结束协同程序");
yield break; // 提前终止协同程序
}
// 如果上面的条件不满足,这将不会执行
yield return new WaitForSeconds(1.0f);
Debug.Log("协同程序又执行了一秒");
}
StartCoroutine(MyNewCoroutine());
}
bool SomeCondition()
{
// 这里是条件的逻辑
return true; // 例如,始终返回 true 来终止协同程序
}
void Start()
{
StartMyCoroutine1();
StartMyCoroutine2();
StartMyCoroutine3();
CloseSpecialCoroutine();
BreakCoroutine();
CloseAllCoroutine();
}
}
8.应用:计时器(顺序计时和倒计时)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TimeCounter : MonoBehaviour
{
IEnumerator TimeConter(int sumSeconds, bool sequence)
{
int time = 0;
if (sequence)
{
while (true)
{
// 顺序计时
// 抵达终止时间
if (time >= sumSeconds)
{
Debug.Log(sumSeconds + "s 时间到(顺序计时)");
yield break;
}
// 延迟一秒执行
yield return new WaitForSeconds(1.0f);
// 修改时间
time++;
// 打印时间
Debug.Log(time);
}
}
else
{
// 倒计时
time = sumSeconds;
while (true)
{
if (time <= 0)
{
Debug.Log(sumSeconds + "s 时间到(倒序计时)");
yield break;
}
Debug.Log(time--);
yield return new WaitForSeconds(1.0f);
}
}
}
private void Start()
{
// 开启计时器协程
Coroutine c1 = StartCoroutine(TimeConter(10, true));
Coroutine c2 = StartCoroutine(TimeConter(3, false));
}
}