Unity中yield return 的使用心得
持续更新中
等待固定时间
当我们要处理一些比较耗费资源的事情并且不需要每帧保持其准确性时,可以使用协程,降低该代码段的使用频率,以达到优化项目的目的。还有一些情况下,我们有时需要在一些固定的时间后做一些操作,等待固定时间也是一个很好的解决方案,比如:塔防游戏中的怪物的生成。
主要用到的类有两个 WaitForSeconds 和 WaitForSecondsRealtime
相同点:都可以用来使协程等待
不同的:前者是缩放的时间,后者是未缩放的时间
什么意思呢??
举例来看:创建一个脚本,添加如下代码
public float timeScale;
public int fps;
public IEnumerator Test1()
{
while (true)
{
Debug.Log("Coming1!!!!!!! " + fps);
yield return new WaitForSeconds(1f);
}
}
public IEnumerator Test2()
{
while (true)
{
Debug.Log("Coming2!!!!!!! " + fps);
yield return new WaitForSecondsRealtime(1f);
}
}
private void Start()
{
fps = 0;
Time.timeScale = timeScale;
StartCoroutine(Test1());
StartCoroutine(Test2());
}
private void Update()
{
fps++;
}
将其绑定到场景对象中,设置timeScale = 1,然后运行
可以看到如下的运行结果:
图一
将time scale 改为2,再次运行,看到如下结果:
图二
细心的朋友可能已经发现,在timescale该为2时,coming1的打印频率上升了,平均间隔比之前短了一半
由此可见,官方所说的缩放是指针对于Time.timeScale,也就是是说,WaitForSeconds的实际等待时间受Time.timeScale影响,而WaitForSecondsRealtime不受Time.timeScale影响。
但是,要注意,WaitForSeconds(x * Time.timeScale)不等于 WaitForSecondsRealtime(x)
因为在Time.timeScale = 1 时,图一中输出的信息显示他们的输出频率并不相同,所以不能如上做出等价。
仔细观察图一,可以发现,Coming2是规律的没秒输出一个,以此可以推出,WaitForSecondsRealtime指的等待时间应该是实际时间,WaitForSeconds指的等待时间应该是游戏时间,我们都知道,游戏时间是受Time.timeScale 影响。
因此,可以确定,WaitForSeconds等待的是游戏的时间,WaitForSecondsRealtime等待的是真实世界的时间。
关于bool变量控制协程等待
对于很多情况下,我们需要等待特定条件后,再使协程继续运行,就需要用到bool变量控制协程运行。
主要用到有两个类 WaitUntil 和 WaitWhile
public bool tmp = false;
private IEnumerator Test1()
{
Debug.Log("Coming1!!!!!!");
yield return new WaitUntil(() => tmp); //tmp为false 等待,为true执行
//yield return new WaitWhile(() => tmp); //tmp为true 等待,为false执行
//yield return tmp; //不可用,不会等待
Debug.Log("Coming2!!!!!!!");
}
跟随null的情况
有时,我们或许需要一些操作,不需要放在Update中,但也需要每帧执行一次,这时候就可以用yield return null,来模拟一个Update。不过有一点需要注意,yield null 是在update 后运行的,如下图官方文档所示。所以在使用时需要仔细考虑代码的执行顺序,避免出现问题。
终止协程
终止协程分为两种,一种是在协程内部终止,一种是外部终止。
内部终止:使用yield break 跳出协程来终止协程。
外部终止:开启协程的方法有一个Coroutine类型的返回值,在开启协程时接收这个值,就可以使用stop的第二个重载关闭协程了。也可以使用StopAllCoroutines 关闭所有协程。
当然,对于另外两种方法来说,有string参数(协程名)的方法可以关闭协程,不过一次只能关闭一个同名协程,而且只能关闭通过string开启的协程。而另一种方法好像并不能关闭协程(或者是我测试的方法不对),所以不推荐使用。
所以,综上所述,推荐使用public Coroutine StartCoroutine(IEnumerator routine); 开启协程,并接收返回值
使用public void StopCoroutine(Coroutine routine); 来关闭协程。
当然,也可以在协程内部通过条件控制,使用yield break 来终止协程 。具体使用,看个人经验及需求吧。