在Unity的学习中,协程(Coroutines)是一个很好用也经常会用到的方法。这里给大家分享一下我自己在学习过程中的所得。
首先我们看一下Unity官方手册的对协程的介绍:
官方手册在这里举了一个例子:(原文)当你调用一个方法(函数)的时候,方法在返回之前就全部执行完成了,这就意味着任何函数内的方法,在一帧之内就执行完成了。所以这些函数没有办法完成一些,包含动画和一些时间事件的函数。例如减少一个物体的alpha值,直到透明。
效果如下:
public class Coroutines_Blog : MonoBehaviour
{
private Image test_Img;
private void Start()
{
test_Img = transform.Find("test_Img").GetComponent<Image>();
Fade();
}
void Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = test_Img.color;
c.a = f;
test_Img.color = c;
}
}
}
在编辑器模式下,我在程序开始的第一帧就暂停了,而Fade方法也在第一帧内就全部执行完毕了。这样显然是没有达到我们想要的效果的。所以协程就出场了,先看Unity手册是怎么介绍的:
(原文)就目前而言,Fade方法实现的效果不是你所期待的,为了能够让渐变可以看见,alpha值的减少需要在一些帧中间完成。然而,这个方法会在一帧内全部完整执行,永远都无法看到中间值,对象会立即消失。虽然我们可以通过添加代码来处理这个这个问题,但是使用协程来解决这类问题会更加方便。协程就像一个函数,能够暂停执行并将控制权返回给Unity,然后他能继续在暂停的地方继续执行。
效果如下:
IEnumerator Fade()
{
for (float f = 1f; f >= 0; f -= 0.1f)
{
Color c = test_Img.color;
c.a = f;
test_Img.color = c;
print(f);
yield return null;
}
}
如上图所示,在编辑器模式下,我还是在第一帧暂停了,并且按帧执行,一共按下了9次,程序执行完毕。这成功的达到了我们所期望的效果。for循环了十次,程序执行了10帧。而这都要归功于yield return 语句,我们发现在yield return 语句在for循环语句内,for循环一共执行了10次,yield return 语句在循环体内也执行了十次。所以大家应该已经看出来了,能够让函数每帧执行的就是yield return 语句。
我换一种写法就一目了然了:
IEnumerator Fade()
{
Color c;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
c = test_Img.color;
c.a -= 0.1f;
test_Img.color = c;
print(c.a);
yield return null;
}
在这里我把for循用yield return 语句隔开,效果和官方的(上面的)代码的效果一样。这里大家应该已经清楚yield return 语句的作用了。我们看一下Unity手册的解释:
(原文)这个函数本质上是使用IEnumerator返回类型,声明了函数,并且在方法内包含了yield return 语句。yield return 语句是执行程序暂停的点,并且是下一帧继续执行的点。想要执行协程函数,需要使用 StartCoroutine函数。
这里要和大家需要注意,协程函数的调用和普通函数不同,需要使用 StartCoroutine语句去调用,有两种方法:
private void Update()
{
if(Input.GetKeyDown("s"))
{
//第一种
StartCoroutine(Fade());
//第二种
StartCoroutine("Fade");
}
}
这两种方法各有优点,大家可以自己去研究,主要是和参数,关闭协程有关
还有一种协程是不需要StartCoroutine语句去调用
//第三种
IEnumerator Start()
{
}
上面的第三种方法就是把Unity内置Start方法的返回值改成IEnumerator,这样就不需要使用StartCoroutine语句去调用,在Start方法应该调用的时候就会去调用(游戏开始的时候),效果和其他协程一样。大家也可以自己去尝试一下,Unity其他的内置方法,能不能通过修改返回值的方法启用协程。
最后还有一点要和大家分享的:先看图
从之前的测试和上图不难看出,协程和线程(Thread)有些类似,但的确是不一样的东西。我给大家举个例子:假如有一条12车道的公路,(就像你有一颗6核12线程的8700k。然而我并没有。呜呜),你每开启一个线程,就相当于你占据一条车道来用,这条车道上就你一辆车。
但是开启协程就像是,在这个你已经占有的车道上,让你的车先靠边停一停,然后换上协程的车上去跑一跑,然后再换回你的车跑一跑,来回切换,模拟出一种两辆车并行的感觉,让你有一种一车道跑出两车道的假象。而这其中的调度是系统帮你完成的,所以协程和线程本质上还是不同的。每个线程上多可以开启多个协程,但线程就只有12个,多开的线程就只能排队(其实也可以通过调度起到协程的效果,一车道两用,但需要程序员自己调度)。
不知道这个例子有没有说明白。
谢谢大家观看,分享让知识变得有意义!后续会继续更新一些我在学习中遇到的有趣的问题。
OJMars
InTheMars