Unity3D协程
协程介绍
Unity的协程系统是基于C#的一个简单而强大的接口 ,IEnumerator,它允许你为自己的集合类型编写枚举器。这一点你不必关注太多,我们直接进入一个简单的例子来看看协程到底能干什么。首先,我们来看一下这段简单的代码
Yield
在Countdown方法中其他的都很好理解,除了两个部分:
l IEnumerator 的返回值
l For循环中的yield return
为了能在连续的多帧中(在这个例子中,3秒钟等同于很多帧)调用该方法,Unity必须通过某种方式来存储这个方法的状态,这是通过IEnumerator 中使用yield return语句得到的返回值,当你“yield”一个方法时,你相当于说了,“现在停止这个方法,然后在下一帧中从这里重新开始!”。
注意:用0或者null来yield的意思是告诉协程等待下一帧,直到继续执行为止。当然,同样的你可以继续yield其他协程,我会在下一个教程中讲到这些。
一些例子
协程在刚开始接触的时候是非常难以理解的,无论是新手还是经验丰富的程序员我都见过他们对于协程语句一筹莫展的时候。因此我认为通过例子来理解它是最好的方法,这里有一些简单的协程例子:
多次输出“Hello”
记住,yield return是“停止执行方法,并且在下一帧从这里重新开始”
IEnumerator SayHelloFiveTimes()
{
Yield return ;
Debug.Log("Hello");
Yield return ;
Debug.Log("Hello");
Yield return ;
Debug.Log("Hello");
Yield return ;
Debug.Log("Hello");
Yield return ;
Debug.Log("Hello");
} //This will do the exact same thing as the above function!
IEnumerator SayHello5Times()
{
for(inti = ; i < ; i++)
{
Debug.Log("Hello");
Yield return ;
}
}
每一帧输出“Hello”,无限循环。。。
通过在一个while循环中使用yield,你可以得到一个无限循环的协程,这几乎就跟一个Update()循环等同
IEnumerator SayHelloEveryFrame()
{
while(true)
{
//1. Say hello
Debug.Log("Hello"); //2. Wait until next frame
Yield return ; }//3. This is a forever-loop, goto 1
}
计时
...不过跟Update()不一样的是,你可以在协程中做一些更有趣的事:
IEnumerator CountSeconds()
{
int seconds = ; while(true)
{
for(float timer = ; timer < ; timer += Time.deltaTime)
Yield return ; seconds++;
Debug.Log(seconds +" seconds have passed since the Coroutine started.");
}
}
这个方法突出了协程一个非常酷的地方:方法的状态被存储了,这使得方法中定义的这些变量都会保存它们的值,即使是在不同的帧中。还记得这个教程开始时那些烦人的计时器变量吗?通过协程,我们再也不需要担心它们了,只需要把变量直接放到方法里面!
开始和终止协程
StartCoroutine(Countdown());
如果我们想要终止所有的协程,可以通过StopAllCoroutines()方法来实现,它的所要做的就跟它的名字所表达的一样。注意,这只会终止在调用该方法的对象中(应该是指调用这个方法的类吧)开始的协程,对于其他的MonoBehavior类中运行的协程不起作用。
如果我们有以下这样两条协程语句:
StartCoroutine(FirstTimer());
StartCoroutine(SecondTimer());
那我们怎么终止其中的一个协程呢?在这个例子里,这是不可能的,如果你想要终止某一个特定的协程,那么你必须得在开始协程的时候将它的方法名作为字符串,就像这样:
StartCoroutine("FirstTimer");
StartCoroutine("SecondTimer"); StopCoroutine("FirstTimer");
协程的参数
抽象化一个协程的第一个方法是给它传递参数,协程作为一个函数方法来说,它自然能够传递参数。这里有一个协程的例子,它在特定的地方输出了特定的信息。
Using UnityEngine;
Using System.Collections; Public class TimerExample : MonoBehaviour
{
Void Start()
{
//Log "Hello!" 5 times with 1 second between each log
StartCoroutine(RepeatMessage(, 1.0f,"Hello!"));
} IEnumerator RepeatMessage(int count,float frequency,string message)
{
for(int i = ; i < count; i++)
{
Debug.Log(message);
for(float timer = ; timer < frequency; timer += Time.deltaTime)
Yield return ; }
}
}
嵌套的协程
在此之前,我们yield的时候总是用0(或者null),仅仅告诉程序在继续执行前等待下一帧。协程最强大的一个功能就是它们可以通过使用yield语句来相互嵌套。
IEnumerator Wait(float duration)
{
for(float timer = ; timer < duration; timer += Time.deltaTime)
Yield return ;
}
IEnumerator SaySomeThings()
{
Debug.Log("The routine has started");
Yield return StartCoroutine(Wait(1.0f));
Debug.Log("1 second has passed since the last message");
Yield return StartCoroutine(Wait(2.5f));
Debug.Log("2.5 seconds have passed since the last message");
} //Our wait function
IEnumerator Wait(float duration)
{
for(float timer = ; timer < duration; timer += Time.deltaTime)
Yield return ;
}
}
第二个方法用了yield,但它并没有用0或者null,而是用了Wait()来yield,这相当于是说,“不再继续执行本程序,直到Wait程序结束”。
控制对象行为的例子
在最后一个例子中,我们就来看看协程如何像创建方便的计时器一样来控制对象行为。协程不仅仅可以使用可计数的时间来yield,它还能很巧妙地利用任何条件。将它与嵌套结合使用,你会得到控制游戏对象状态的最强大工具。
运动到某一位置
对于下面这个简单脚本组件,我们可以在Inspector面板中给targetPosition和moveSpeed变量赋值,程序运行的时候,该对象就会在协程的作用下,以我们给定的速度运动到给定的位置。
usingUnityEngine;
Using System.Collections; Public class MoveExample : MonoBehaviour
{
ublic Vector3 targetPosition;
ublic float moveSpeed; Void Start()
{
StartCoroutine(MoveToPosition(targetPosition));
} IEnumerator MoveToPosition(Vector3 target)
{
while(transform.position != target)
{
transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);
Yield return ;
}
}
}
这样,这个程序并没有通过一个计时器或者无限循环,而是根据对象是否到达指定位置来yield。
按指定路径前进
我们可以让运动到某一位置的程序做更多,不仅仅是一个指定位置,我们还可以通过数组来给它赋值更多的位置,通过MoveToPosition() ,我们可以让它在这些点之间持续运动。
Using UnityEngine;
Using System.Collections; Public class MoveExample : MonoBehaviour
{
ublic Vector3[] path;
ublic float moveSpeed; Void Start()
{
StartCoroutine(MoveOnPath(true));
} IEnumerator MoveOnPath(bool loop)
{
do
{
foreach(var point in path)
Yield return StartCoroutine(MoveToPosition(point));
}
while(loop);
} IEnumerator MoveToPosition(Vector3 target)
{
while(transform.position != target)
{
transform.position = Vector3.MoveTowards(transform.position, target, moveSpeed * Time.deltaTime);
Yield return ;
}
}
}
我还加了一个布尔变量,你可以控制在对象运动到最后一个点时是否要进行循环。
把Wait()程序加进来,这样就能让我们的对象在某个点就可以选择是否暂停下来,就像一个正在巡逻的AI守卫一样,这真是锦上添花啊!
注意:
如果你刚接触协程,我希望这两个教程能帮助你了解它们是如何工作的,以及如何来使用它们。以下是一些在使用协程时须谨记的其他注意事项:
- l 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程;
- l 多个协程可以同时运行,它们会根据各自的启动顺序来更新;
- l 协程可以嵌套任意多层(在这个例子中我们只嵌套了一层);
- l 如果你想让多个脚本访问一个协程,那么你可以定义静态的协程;
- l 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样;
- l 如果你的程序需要进行大量的计算,那么可以考虑在一个随时间进行的协程中处理它们;
- l IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用;
- l 目前在Unity中没有简便的方法来检测作用于对象的协程数量以及具体是哪些协程作用在对象上。
Unity3D协程的更多相关文章
- Unity3D协程yield的理解
Unity3D的协程概括地将就是:对于一段程序,你可以加上yield标明哪里需要暂停,然后在下一帧或者一段时间后,系统会继续执行这段代码.协程的作用:①延迟一段时间执行代码.②等某个操作完成之后再执行 ...
- Unity3d 协程的注意问题(新手须注意,老手须加勉)
关于unity3d的协程,非常的好用,比如等待几秒执行,等待下一帧执行等! 但是也有潜在的问题: 1.协程是单线程的,在主线程中完成 2.如果发现yield, 那么这一帧会结束,那么等下一帧调用此脚本 ...
- Unity3D 协程 浅谈
协程 理解:协程不是线程,也不是异步执行(知道就行). 1.协程和MonoBehaviour的Update函数一样,也是在MainThread中执行的(一定得明白这句话意思). void Start ...
- Unity3d 协程
参考文章: http://blog.csdn.net/onafioo/article/details/48979939 http://www.cnblogs.com/zhaoqingqing/p/37 ...
- Unity3D 协程 Coroutine
协程(Coroutine)的概念存在于很多编程语言,例如Lua.ruby等.而由于Unity3D是单线程的,因此它同样实现了协程机制来实现一些类似于多线程的功能,但是要明确一点协程不是进程或线程,其执 ...
- Unity3D 协程的介绍和使用
我是快乐的搬运工 http://blog.csdn.net/u011397120/article/details/61236055 ---------------------------------- ...
- [转]Unity3D协程介绍 以及 使用
作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/introduction-to-coroutines/ 在Unity中,协 ...
- Unity3d 协程、调用函数、委托
(一)协程 开启方法:StartCoroutine("函数名"): 结束方法StopCoroutine("函数名"),StopAllCoroutines(); ...
- Unity3D协程介绍 以及 使用
作者ChevyRay ,2013年9月28日,snaker7译 原文地址:http://unitypatterns.com/introduction-to-coroutines/ 在Unity中,协 ...
随机推荐
- rabbitmq 基本信息
两个或多个系统间需要通过定时任务来同步数据,异构系弘的不同进程间想互调用 通讯的问题,可以用rabbitmq解决.rabbitmq擅长解决多系统 异构系统音的数据交换,也可以用于系统间服务的相互调用R ...
- 字符设备驱动笔记——中断方式按键驱动之linux中断处理结构(五)
一.单片机下的中断处理 )分辨是哪一个中断 )调用处理函数 )清中断 二.linux下的中断处理 1)/arch/arm/kernel/irq.c asmlinkage void __exceptio ...
- 常用的兼容IE和火狐FF等浏览器的js方法(js中ie和火狐的一些差别)
介绍了网页上常用的IE/火狐兼容性该页的做法,并给出了代码,相当实用了.为了方便大家阅读代码,以下以 IE 代替 Internet Explorer,以 MF/FF 代替 Mozzila Firefo ...
- 屏幕亮度(XE10.1+WIN8.164)
相关资料: http://bbs.csdn.net/topics/390664310 实例代码: unit Unit1; interface uses Winapi.Windows, Winapi.M ...
- 【WPF/C#】使用BackgroundWorker实现多线程/异步操作
做WPF时需要做一个异步加载时的Loading遮罩,搜Stackoverflow看到很多方法,看到了这个插件: BusyIndicator in the extended WPF Toolkit 同时 ...
- am335x ti SDK6.0 kernel 时钟源码文件记录
源码流程记录 板级文件开始 // arch/arm/mach-omap2/board-aplex_cmi_at101.c MACHINE_START(APLEX_CMI_AT101, "ap ...
- spring4.1.6配置quartz2.2.1(maven) <转>
Spring3.0不支持Quartz2.0,因为org.quartz.CronTrigger在2.0从class变成了一个interface造成IncompatibleClassChangeError ...
- HTML——动画效果:图片循环横向播放
一.html <!DOCTYPE HTML> <html> <head> <title>Home</title> <link href ...
- 内核定时器timer_list
内核在时钟中断发生后执行检测各个定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行.实质上,时钟中断处理程序会唤起TIMER_SOFTIRQ软中断,运行当前处理器上到期的所有定时器.lin ...
- java web中读取properties文件时的路径问题
在web开发时,难免会有一些固定的参数,我们一般把这些固定的参数存在properties文件中,然后用的时候要读出来.但经常出现一些错误,找不到相应的路径,所以,今天特地讲一些如何正确获得路径. 首先 ...