Hello,这里是Karmotrine之众所周知,Unity的协程函数一直Unity匪夷所思的难点之一。至少它在一开始就困扰了我很久.但是在现在,我好像悟了(bushi,就把自己的理解打在公屏上,希望dalao轻喷。想不到吧我竟然更了哈哈哈哈哈哈好吧其实我也妹想到
Unity的协程和线程
在谈协程函数之前,我们先了解一下协程和线程的区别。线程相当于Unity中一直在进行的事物,只有在程序或者脚本结束时才能停止。比如说Update
函数,它在程序开始时的每一帧都会被调用,而不是Start
或者Awake
那样只被调用一次。而协程函数就不一样了。它可以随意在某个时间被启用,也可以持续随便一段时间,也可以在任意时间暂停又开始,也可以在任意时间结束。比如说之前文章里的对话框系统。我只想这个脚本在对话框系统启用时才调用它,然后一结束就关闭。这个时候使用线程显然是不太行的,用协程函数写简直无比舒适。demone,协程函数也有所谓的缺点,它的开始,暂停,结束都需要我们手动进行操作,而不像线程那样只要一开程序就不用管了。而且协程改变的参量会被线程改变。比如说我协程写了冲刺给物体的速度赋值,然后它就被线程里的Move
函数改回来了,导致根本冲不动(????????)的事情发生。
综上所述,协程和线程的主要区别为:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。而线程就不需要管那么多,写就完事了,但是我们也要做好不能随意让它停止或者不干扰协程运行的准备。
IEnumerator
的神仙妙用
在了解了协程的主要功能后,我们就可以来聊聊协程函数了?然而并不行,我们还要从一个功能开始:
如何完成cd的冷却/倒计时?
众所周知,一个游戏肯定要有cd:闪现有cd,对话框一个字一个字出来有cd,就连手冲都要有cd(我有个朋友想要这个游戏),但是如何实现这个功能呢?这显然是easygame:Update
函数会每一帧调用,这样就能实现时间的流逝,而不像其他函数就调用一帧,指过一帧的时间就咋瓦鲁多了,这还怎么整嘛。所以,倒计时系统就应该这么写:
1 | void Update() |
但是,这搞一个倒计时还好,但如果我要整114514个呢?(怎么臭起来了)这样我们只能多写几个变量一起算?
1 | void Update() |
demone,这样做岂不是有亿点繁琐,这样还是3个,如果是114514个(捂鼻)该怎么办?那不如写个循环???
1 | for(float timer = 114514; timer >= 0; timer -= Time.deltaTime) |
欸,你这个写的就正好和协程一样了,因为瓦塔西的协程函数正好可以做到这一点!
协程函数的主要介绍
那我们就以协程函数来写一个倒计时系统吧(
1 | void Start() |
在这中间:
StartCoroutine(Countdown())
就指的是开始协程函数(协程函数这么nb,自然要有特殊的打开方式
IEnumerator
是协程函数特有的开头,就类似于void
、int
之类的
for
函数和Debug
没什么好说的,也就是倒计时然后提醒而已。
然后结束协程函数就是StopCoroutine(Countdown())
了,然后如果要全部停止就可以打StopAllCoroutine()
,但是我们不是经常用,因为协程函数也和其它函数一样,一到末尾就结束了。
接下来才是我们重中之重的:
yield return 0
了。
wtf is yield return???
关于yield return 0
真的困扰了我很久,对于它的功能,我只能说是:停止执行方法,并且在下一帧从这里重新开始。这是什么意思呢?我们先看下面一段代码:
1 | IEnumerator SayWDNMD() |
那么在调用这个方法的结果是什么呢,由于我懒得开Unity截图结果,再加上可能会出现偏差,所以这波直接高中理科实验吧:
1 | 时间 内容 |
那么结果就显而易见了,每当yield return 0
调用一次就会暂停0.02s(顺便一提,其中的0没有任何作用,改成1也是停一帧,2也是一帧,114514也是一帧)。我们把协程函数比喻你在彩虹六号:围攻作为防守的一次对局的各种动作,那么yield return 0
就相当于lion开了技能时你的表现。你想继续行动,但是yield return 0
卡着你硬要你等一帧才能继续。
常见提问:
为什么会出现偏差?
因为Unity每一帧的时间是不一样的,可能这帧时间为0.02s,但下一帧就可能不是。因此Unity提供了Update
和FixedUpdate
两个函数,其中FixedUpdate
函数就是在每个固定一帧的时间内调用。这里为了方便就固定是0.02s(明明就是你想偷懒)
可不可以不停一帧,改成其他时间?
可以的,但是要改成yield return new WaitForSeconds(Time)
,其中Time表示你要暂停的时间,以秒为单位。
小细节,大作用
那么我们这样卡一下到底有什么用呢?作用可以说是很大了。因为:
我们可以将需要时间流逝的函数写成了一种单独的方法,不需要一股脑全部丢进Update
函数里。并且我们还可以随时停止并规定停止的时间,不像Update
函数一开始就停不下来。
协程函数最为简单的应用就是作为倒计时使用,比如进行冲刺后的冷却:
1 | IEnumerator Recover() |
众所周知,我们在游戏时肯定不会一直冲啊,(一直冲现实也受不了吧)放进Update
函数里肯定不太合适(但实际上放进去是可以的,写个if放里面就差不多,但是作为方法储存代码肯定更好看一点)。而这时候作为方法写进协程函数简直是再好不过了。
常见提问:
yield return 0
放在循环里是否必要?
依照我个人理解,其实yield return 0
放在哪里都可以。因为它只是借用了协程函数可以按照时间变化而变化的好处来写的,其实并不需要在哪里停一下。但是为什么要写呢,因为Unity规定协程函数至少要有一个yield
进行结束,所以就随便放呗~~(大概)~~~
但是要注意一点,在计时的循环里一定要存在yield return 0
这个语句,因为它的功能是在这一帧停止,然后在下一帧开始。如果没有加的话,它就一直继续,然后就能在一帧内处理你设定的几秒的计时,也就不存在你的计时了。。。
一些其他要注意的
协程函数可以嵌套🐎?
可以的,比如在冲刺和冲刺后回复的两个协程函数
1 | IEnumerator CountingDash() |
注意
虽然协程函数有时候很好用,但是建议没有掌握的人还是全部丢进Update
函数里,毕竟我之前因为协程已经卡死114514次,重做了114514次对话框了。。。
其他的以后想到了在更qwq
差不多讲完了,祝民那桑用协程函数“怎么那么熟练啊”,Unity不卡崩~
友情链接
csdn的各种协程函数教程:(自己上官网看吧)这nm是友情链接???
- 本文链接:https://karmotrine.fun/2021/04/11/%E5%85%B3%E4%BA%8EUnity%E5%8D%8F%E7%A8%8B%E5%87%BD%E6%95%B0%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%90%86%E8%A7%A3/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。
若没有本文 Issue,您可以使用 Comment 模版新建。