Unity3D提供的武器Coroutine,如果您因为一些所谓的”坑“,禁止自己或者他人使用,你将会失去一把锋利的武器,同时你也会”后悔“。
PS:当然并没有这么可怕..下面我们通篇介绍下Unity强大的“走走停停,分解压力”功能Coroutine.
最简单的解释:
你想在程序中每隔“多长时间”或者“下一帧”过后才继续执行一个程序块的剩余部分内容。
举几个使用场景:
[1]我们有一个复杂计算量非常大的处理逻辑,如果在同一帧执行,一定会导致游戏的卡顿,需要将这些大的逻辑处理“分割”到“每隔一帧”或者“2s过后”执行的小块中。
[2].网络传输的时候,需要等待文件下载完毕过后才能执行其他的任务,使用Coroutine可以帮助我们实现“xx事件完成过后,继续执行该程序块后续的内容”
[3].UI方面我们会遇到一次性加载过多的Item导致手机卡顿的问题,使用Coroutine可以帮助我们将加载过程进行“分割”,缓解卡顿的问题,给加载过程一个“一帧,或者1s的歇息”
[4].定时器
[5].执行一个带着时间点的函数队列
按照”走走停停“思想我们可以想出来很多的适用场景
为什么用?不需要复杂的多线程,不需要你去专门做一个效果定时器,自己去累加时间管理时间,最简单的方式就是使用Unity3D提供的Coroutine机制。(它并不是多线程,它和咱们的其他Script一样在同一个主线程执行)
总结:Coroutine就是提供给我们:方便暂停一个函数的执行(我们可以多次的暂停如果需要),直到我们设定的”条件“满足过后,继续执行该函数剩余的操作。在一个函数中你可以任意”暂停“和”继续执行“,只需要将你的代码块填到对应的位置即可。
既然Coroutine可以帮助我们完成这么多内容为何要抛弃它而不去了解它呢?
-------------------------------------------------------------- 步入正题ing---------------------------------------------------------------------------
Coroutine如果要满足我们的需求:”等“-”执行“-”等“-”执行“......需要什么特性呢?
1.对于我们一般的函数,当我们的函数执行完毕过后(return或者执行结束),所有的函数局部变量和”状态“都会被释放,但是对于Coroutine需要让一个函数多次的return,直到所有的return结束过后才会将函数的状态清空。
Coroutine In Detail?
如果对Coroutine内部的实现感兴趣,能够更好地掌握Coroutine,可以通过Google搜索”Unity3d coroutine in detail“可以搜索到很多有关介绍Coroutine使用的教程
这是其中一篇牛人的猜测(不需要翻墙也能看到,只不过有点慢...)写了有关Coroutine的内部实现猜测,牛人的猜测一般都是”真相“.
借助 牛人的猜测,我在这里简单的总结下Coroutine的实现原理:内部其实是一个维护的迭代器(当你的协同报出错误的时候你可以看到提示的内容,会有一个MoveNext样子的错误提示,也可以验证)
你想在程序中每隔“多长时间”或者“下一帧”过后才继续执行一个程序块的剩余部分内容。
举几个使用场景:
[1]我们有一个复杂计算量非常大的处理逻辑,如果在同一帧执行,一定会导致游戏的卡顿,需要将这些大的逻辑处理“分割”到“每隔一帧”或者“2s过后”执行的小块中。
[2].网络传输的时候,需要等待文件下载完毕过后才能执行其他的任务,使用Coroutine可以帮助我们实现“xx事件完成过后,继续执行该程序块后续的内容”
[3].UI方面我们会遇到一次性加载过多的Item导致手机卡顿的问题,使用Coroutine可以帮助我们将加载过程进行“分割”,缓解卡顿的问题,给加载过程一个“一帧,或者1s的歇息”
[4].定时器
[5].执行一个带着时间点的函数队列
按照”走走停停“思想我们可以想出来很多的适用场景
为什么用?不需要复杂的多线程,不需要你去专门做一个效果定时器,自己去累加时间管理时间,最简单的方式就是使用Unity3D提供的Coroutine机制。(它并不是多线程,它和咱们的其他Script一样在同一个主线程执行)
总结:Coroutine就是提供给我们:方便暂停一个函数的执行(我们可以多次的暂停如果需要),直到我们设定的”条件“满足过后,继续执行该函数剩余的操作。在一个函数中你可以任意”暂停“和”继续执行“,只需要将你的代码块填到对应的位置即可。
既然Coroutine可以帮助我们完成这么多内容为何要抛弃它而不去了解它呢?
-------------------------------------------------------------- 步入正题ing---------------------------------------------------------------------------
Coroutine如果要满足我们的需求:”等“-”执行“-”等“-”执行“......需要什么特性呢?
1.对于我们一般的函数,当我们的函数执行完毕过后(return或者执行结束),所有的函数局部变量和”状态“都会被释放,但是对于Coroutine需要让一个函数多次的return,直到所有的return结束过后才会将函数的状态清空。
Coroutine In Detail?
如果对Coroutine内部的实现感兴趣,能够更好地掌握Coroutine,可以通过Google搜索”Unity3d coroutine in detail“可以搜索到很多有关介绍Coroutine使用的教程
这是其中一篇牛人的猜测(不需要翻墙也能看到,只不过有点慢...)写了有关Coroutine的内部实现猜测,牛人的猜测一般都是”真相“.
借助 牛人的猜测,我在这里简单的总结下Coroutine的实现原理:内部其实是一个维护的迭代器(当你的协同报出错误的时候你可以看到提示的内容,会有一个MoveNext样子的错误提示,也可以验证)
IEnumerator e = TellMeASecret();
while(e.MoveNext())
{
// If they press 'Escape', skip the cutscene
if(Input.GetKeyDown(KeyCode.Escape)) { break; }
}
IEnumerator TellMeASecret()
{
PlayAnimation("LeanInConspiratorially");
while(playingAnimation)
yield return null;
Say("I stole the cookie from the cookie jar!");
while(speaking)
yield return null;
PlayAnimation("LeanOutRelieved");
while(playingAnimation)
yield return null;
}
上代码应该看的很清楚,这个是文中进行的假设,将我们执行的Block代码块组织成一个迭代器,当执行完一个程序块过后,迭代器会自动执行MoveNext操作,我们也可以手动暂停终止。
怎么定?(YieldInstruction)
给我们提供的一些”Instructions“
我们也可以定义自己的YieldInstruction,确定如何进行”等待“
怎么启动:
(StartCoroutine)
怎么暂停:(StopCoroutine?+yield break;)
只有使用string类型作为StartCoroutine启动的协同才能够被StopCoroutine进行暂停,如果你想暂停该GameObject启用的所有Coroutine直接用StopAllCoroutines();
当然也可以使用yield break;根据逻辑条件内部终止一个coroutine;
int waitCount = 5;
private IEnumerator ExcuteSomething()
{
while(true)
{
yield return new WaitForSeconds(1f);
waitCount --;
//TODO
if (waitCount >= 0)
{
//TODO
Debug.Log("Wait count is " + waitCount);
}
else
yield break;
}
}
下面我们举一些简单的例子说明Coroutine的使用方法和一些注意事项:
[1]最基本使用
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
void Start() {
print("Starting " + Time.time);
StartCoroutine(WaitAndPrint(2.0F));
print("Before WaitAndPrint Finishes " + Time.time);
}
IEnumerator WaitAndPrint(float waitTime) {
yield return new WaitForSeconds(waitTime);
print("WaitAndPrint " + Time.time);
}
}
[2]定时器
private IEnumerator CountDown(float fTime)
{
Debug.Log("Make a timer ftime is " + fTime);
for(float timer = fTime;timer >= 0;timer -= Time.deltaTime)
yield return 0;
Debug.Log("After " + fTime + " log this message.");
//TODO do something you want
}
[3]指定一个时间执行函数序列,要知道其中的一个”坑“如果使用WaitForSeconds();
你的TimeScale被设置成了0,那么你的Coroutine将不会返回除非你的TimeScale重新设置成了非0值,例子中展示了如何让启动的Coroutine不受到TimeScale的影响
public class CallEvents : MonoBehaviour
{
public class CellEventFuntion
{
public float fTime;
public CallBack callBack;
}
public delegate void CallBack();
public delegate void CallBack<T>(T t);
public delegate void CallBack<T, K>(T t1, K t2);
public List<CellEventFuntion> eventList = new List<CellEventFuntion>();
//设定一个根据时间调用的队列(每隔一定的时间调用不同的回调函数)
public void CallFunctionListByTimes()
{
eventList.Clear();
//添加新的Event到我们的list中
CellEventFuntion newFunction = new CellEventFuntion();
newFunction.fTime = 2.0f;
newFunction.callBack = Function01;
CellEventFuntion newFunction02 = new CellEventFuntion();
newFunction02.fTime = 2.0f;
newFunction02.callBack = Function02;
CellEventFuntion newFunction03 = new CellEventFuntion();
newFunction03.fTime = 2.0f;
newFunction03.callBack = Funciton03;
CellEventFuntion newFunction04 = new CellEventFuntion();
newFunction04.fTime = 5.0f;
newFunction04.callBack = Function04;
eventList.Add(newFunction);
eventList.Add(newFunction02);
eventList.Add(newFunction03);
eventList.Add(newFunction04);
//进行执行List中所有的Event
//将会受到TimeScale的影响
//StartCoroutine(_CallFuctionListByTimes());
//不会受到TimeScale的影响
StartCoroutine(_CallFunctionListByTimeIgnoreTimeScale());
}
private IEnumerator _CallFuctionListByTimes()
{
foreach (CellEventFuntion fuction in eventList)
{
float fTime = fuction.fTime;
yield return new WaitForSeconds(fTime);
if (fuction.callBack != null)
fuction.callBack();
}
}
private IEnumerator _CallFunctionListByTimeIgnoreTimeScale()
{
foreach (CellEventFuntion function in eventList)
{
float fTime = function.fTime;
yield return StartCoroutine(_WaitTimeEnd(fTime));
if (function.callBack != null)
function.callBack();
//can be replaced by function.(这段是和使用_WaitTimeEnd一样的效果)
//float start = Time.realtimeSinceStartup;
//while (Time.realtimeSinceStartup < start + fTime)
//{
// yield return null;
//}
}
}
private IEnumerator _WaitTimeEnd(float fTime)
{
float start = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup < start + fTime)
{
yield return null;
}
}
private void Function01()
{
//Debug.Log("--Call Function01**" + Time.time);
Debug.Log("--Call Function01**" + Time.realtimeSinceStartup);
}
private void Function02()
{
//Debug.Log("--Call Function02**" + Time.time);
Debug.Log("--Call Function02**" + Time.realtimeSinceStartup);
}
private void Funciton03()
{
//Debug.Log("--Call Function03**" + Time.time);
Debug.Log("--Call Function03**" + Time.realtimeSinceStartup);
}
private void Function04()
{
//Debug.Log("--Call Function04**" + Time.time);
Debug.Log("--Call Function04**" + Time.realtimeSinceStartup);
}
public void PauseGame()
{
Time.timeScale = 0.0f;
}
public void Resume()
{
Time.timeScale = 1.0f;
}
void OnDestroy()
{
StopAllCoroutines();
}
}
你可以自己写一个测试用例,点击按钮把TimeScale设置不同的值,然后调用不同的启动函数,看看自己的Coroutine是否受到TimeScale的影响.
一些要注意的Notes或者说使用的”坑“:
多看文档
1.TimeScale的问题:使用了WaitForSeconds()如果将TimeScale设置0,则不会返回;.
2.Coroutine不是多线程,它和我们其他的脚本一样在主线程中顺序执行,它只是将我们的工作放置到”时间段“中执行而已.在Unity脚本执行流程中的具体位置可以参考文档
3.当你的GameObjet被删除的时候,对应的Coroutines也会被终止(如果你还担心,那就在脚本OnDestroy的时候StopAllCoroutines就行了).
4.没写一个Coroutine的时候都要看看终止的条件是什么,让Coroutine自己”自生自灭“是最好的写法,所以写的时候注意退出条件,否则除非给你的工程带来一些奇怪的问题(因为工程中的某个Coroutine一直在执行!).
多看文档
1.TimeScale的问题:使用了WaitForSeconds()如果将TimeScale设置0,则不会返回;.
2.Coroutine不是多线程,它和我们其他的脚本一样在主线程中顺序执行,它只是将我们的工作放置到”时间段“中执行而已.在Unity脚本执行流程中的具体位置可以参考文档
3.当你的GameObjet被删除的时候,对应的Coroutines也会被终止(如果你还担心,那就在脚本OnDestroy的时候StopAllCoroutines就行了).
4.没写一个Coroutine的时候都要看看终止的条件是什么,让Coroutine自己”自生自灭“是最好的写法,所以写的时候注意退出条件,否则除非给你的工程带来一些奇怪的问题(因为工程中的某个Coroutine一直在执行!).
5.disable的GameObject没办法启动协同.
6.多找资料,多实践,如果你害怕Coroutine,听别人说”坑“多,那他真的是在”坑“You.
6.多找资料,多实践,如果你害怕Coroutine,听别人说”坑“多,那他真的是在”坑“You.
网上找了很多Coroutine相关的资料,一些不错的献上
[1]一些使用
注意事项http://answers.unity3d.com/questions/751178/when-using-coroutines-what-should-i-be-wary-of.html
[2]深入Coroutine的
文章http://www.massimpressionsprojects.com/dev/altdevblog/2011/07/07/unity3d-coroutines-in-detail/
学习和使用Coroutine一定会给你带来开发效率,因为它是个很犀利的武器,装入武器库吧,别害怕,别逃避,如果发现真的是”坑“,那就写封信给Unity开发公司投诉吧.[哈哈哈]
希望看到这篇文章的人,能够对Coroutine进一步的了解,把Coroutine放入自己的武器库.
有什么想法,建议,
错误,希望指出和交流.
.CodeForFreedom.