如何控制unity 协程 计时器的顺序

Liu_guozhu 的BLOG
用户名:Liu_guozhu
文章数:41
评论数:14
访问量:24836
注册日期:
阅读量:5863
阅读量:12276
阅读量:383467
阅读量:1074971
51CTO推荐博文
Coroutine:协同程序(简称:协程),参考网络给出以下两个定义供参考。&1&&协同程序,即在主程序运行时同时开启另一段逻辑处理,来协同当前程序的执行。换句话说,开启协同程序就是开启一个模拟线程。 [作者注: 不是真正的线程]&2& 是一种很古老的编程模型,以前的操作系统里进程调度里用到过,现在操作系统的进程调度都是根据时间片和优先级来进行轮换,以前是要程序自己来释放cpu的控制权,一直不释放一直也就占用着cpu,这种要求程序自己来进行调度的编程模型应该就叫“协程”。&语法:function StartCoroutine (routine:IEnumerator) : Coroutine function StartCoroutine (methodName:string, value : object = null) :&Coroutinefunction StopCoroutine (methodName:string) : void function StopAllCoroutines () : void&示例: &&& void Start()&&& {&&&&&&& print("1: " + Time.time);&&&&&&& StartCoroutine(WaitAndPrint(2.0F));&&&&&&& print("3: " + Time.time);&&& }&&&& IEnumerator WaitAndPrint(float waitTime)&&& {&&&&&&& yield return newWaitForSeconds(waitTime);&&&&&&& print("2: " +Time.time.ToString());}&输出结果: 1: 03: 02: 2.004061&&Invoke:调用函数,指每隔多少时间执行一次某方法。InvokeRepeating: 重复调用函数,指在给定时间后指定间隔时间后重复执行方法。&语法:function Invoke (methodName: string, time :float) : void functionInvokeRepeating (methodName :String,time : float, repeatRate : float) : void function CancelInvoke () : void function IsInvoking (methodName: string) :bool&示例: &&& private int _IntNumber = 0;&&&&&&&&&&&&&&&&&&&&&&&&&&& //累加数字&&& &&& void Start()&&& {&&&&&&& //this.Invoke("DisplayNum",1F);&&&&&&&&&&&&&&&&&& //只调用一次&&&&&&&this.InvokeRepeating("DisplayNum", 0F, 1F);&&&&&&& //间隔1秒反复调用&&& }&&&& //每隔一定时间,进行自增想加数字且输出结果&&& void DisplayNum()&&& {&&&&&&& Debug.Log("自增数字: " + (++_IntNumber));}输出结果:&& 自增数字:1自增数字:2自增数字:3自增数字:4自增数字:5自增数字:6(结果会无限循环下去...)&&以上技术在Unity 实际编程过程中被大量应用,个人体会Invoke 与 InvokeRepeating 为 Coroutine 的简化写法,是按照事先定义好的固定时间间隔进行调用的编程模型。优点是结构与语法编写简单。Coroutine 则是更加完善,功能更加强大的编程模型。在处理需要制作多种间隔时间,从而达到一定功能的地方有良好的应用效果。以下是开发与教学过程中实际的应用场景举例:&开发游戏倒计时案例。 &(示例 1.1) &using UnityEusing System.C&public class GUIProgress_LevelOne : MonoBehaviour {&&&& publicGUISkin projectWukongS&&&&&&&&&&&&&&&&&&&&&//项目皮肤&&& publicTexture2D TexGameOver_F&&&&&&&&&&&&&&&&&&//游戏结束贴图。&&& publicTexture2D TexNumberO&&&&&&&&&&&&&&&&&&&&&&&&//数字贴图1&&& publicTexture2D TexNumberT&&& publicTexture2D TexNumberT &&& //项目信息贴图&&& publicTexture2D TexProjectL&&&&&&&&&&&&&&&&&&&&&&//项目Logo&&& publicTexture2D TexRedD&&&&&&&&&&&&&&&&&&&&&&&//红宝石&&&& privatebool _BoolIsDrawCountDownFlag =&&&&&&&&//绘制倒计时数字&&& private int_IntDrawNumber = 3;&&&&&&&&&&&&&&&&&&&&&&&//绘制具体数字&& &&& &&&& void Start() &&& {&&&&&&& //启动游戏倒计时&&&&&&&StartCoroutine(DrawGameStartCountDown());&&& }//Start_end&&&& //游戏开始倒计时&&& IEnumeratorDrawGameStartCountDown()&&& {&&&&&&& //1秒的准备&&&&&&& yieldreturn new WaitForSeconds(1F);&&&&&&&& //显示数字3&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&&_IntDrawNumber = 3;&&&&&&& yieldreturn new WaitForSeconds(1F);& //停留1秒的查看时间&&&&&&& //空白时间&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&& yieldreturn new WaitForSeconds(0.5F);&&&&&&&& //显示数字2&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&&_IntDrawNumber = 2;&&&&&&& yieldreturn new WaitForSeconds(1F);& &&&&&&& //空白时间&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&& yieldreturn new WaitForSeconds(0.5F);&&&&&&&& //显示数字1&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&&_IntDrawNumber = 1;&&&&&&& yieldreturn new WaitForSeconds(1F);&&&&&&&_BoolIsDrawCountDownFlag =&&&&&&&& //开始游戏。&&&&&&&GlobalInfoManger.CurrentGameState = GameState.P&&& }&&&& void OnGUI()&&& {&&&&&&&GUI.skin = projectWukongS&&&&&&&& /* 显示项目信息 */&&&&&&& //项目Logo&&&&&&&GUI.DrawTexture(new Rect(Screen.width / 2 - TexProjectLogo.width/2, 0,TexProjectLogo.width, TexProjectLogo.height), TexProjectLogo);&&&&&&& //红宝石&&&&&&&GUI.DrawTexture(new Rect(0, 0,50,50), TexRedDiamond);&&&&&&& //红宝石当前的数量&&&&&&&GUI.Label(newRect(50,0,100,100),GlobalInfoManger.IntRedDiamonds.ToString());&&&&&&& //显示游戏时间&&&&&&&GUI.Label(new Rect(170, 0,150,80),"Time:");&&&&&&& //当前游戏的时间&&&&&&&GUI.Label(new Rect(300, 0, 100, 100),GlobalInfoManger.IntGameTime.ToString());&&&&&&&&& //绘制游戏不成功贴图画面&&&&&&& if(GlobalInfoManger.CurrentGameState==GameState.GameOver)&&&&&&& {&&&&&&&&&&&GUI.DrawTexture(new Rect(Screen.width / 2 - TexGameOver_Falure.width /2, Screen.height / 2 - TexGameOver_Falure.height / 2, TexGameOver_Falure.width,TexGameOver_Falure.height), TexGameOver_Falure);&&&&&&& }&&&&&&&& #region绘制倒计时数字贴图&&&&&&& //绘制倒计时数字贴图&&&&&&& if(_BoolIsDrawCountDownFlag)&&&&&&& {&&&&&&&&&&& if(_IntDrawNumber == 3)&&&&&&&&&&& {&&&&&&&&&&&&&&&GUI.DrawTexture(new Rect(Screen.width / 2 - TexNumberThree.width / 2,Screen.height / 2 - TexNumberThree.height / 2, TexNumberThree.width,TexNumberThree.height), TexNumberThree);&&&&&&&&&&& }&&&&&&&&&&&else if (_IntDrawNumber == 2)&&&&&&&&&&& {&&&&&&&&&&&&&&&GUI.DrawTexture(new Rect(Screen.width / 2 - TexNumberTwo.width / 2,Screen.height / 2 - TexNumberTwo.height / 2, TexNumberTwo.width,TexNumberTwo.height), TexNumberTwo);&&&&&&&&&&& }&&&&&&&&&&&else if (_IntDrawNumber == 1)&&&&&&&&&&& {&&&&&&&&&&&&&&&GUI.DrawTexture(new Rect(Screen.width / 2 - TexNumberOne.width / 2,Screen.height / 2 - TexNumberOne.height / 2, TexNumberOne.width,TexNumberOne.height), TexNumberOne);&&&&&&&&&&& }&&&&&&& }&&&&&&&#endregion&&& }//OnGUI_end&&& &}//Class_end&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&图 1.1 &&示例 1.1 参考图1.1进行理解,基本原理如下:&以上定义的协程方法 “DrawGameStartCountDown()” 主要是运用& “_BoolIsDrawCountDownFlag”与“_IntDrawNumber ” 这两个字段对 OnGUI() 事件函数中“GUI.DrawTexture ” 绘制贴图的时间间隔与绘制内容进行控制。&&再举一例:&开发游戏战绩数值统计案例。 &(示例 1.2) &usingUnityEusingSystem.C&public classGUI_CountResult : MonoBehaviour {&&&& public GUISkin ProjectGUIS&&&&&&&&&&&&&&&&&&&&&&&& //项目皮肤&&& public Texture2D TexGameB&&&&&&&&&&&&&&&&&&& //游戏背景贴图&&& public Texture2D TexRedD&&&&&&&&&&&&&&&&&&&&&& //红宝石贴图&&& public Texture2D TexBlueD&&&&&&&&&&&&&&&&& &&&&//蓝宝石贴图&&& public string StrScenesN&&&&&&&&&&&&&&&&&&&&&&&&&& //场景名称。&&& public Texture2D TexReP&&&&&&&&&&&&&&&&&&&&&&&&&&& //"重玩"贴图&&&& private bool _boolRePlay =&&&&&&&&&&&&&&&&&&&&& //是否重玩&&& private bool _boolExit =&&&&&& &&&&&&&&&&&&&&&&&//是否退出&&&& //开发动态文字显示&&& private int _IntRunedTime = 0;&&&&&&&&&&&&&&&&&&&&&&&& //玩家跑过的时间。&&& private bool _BoolIsDisplayRunedTime =&&&&&&&&& //是否显示跑过的里程&&& private int _IntRedDiamondNum = 0;&&&&&&&&&&&&&&&&&&&& //玩家跑过的时间。&&& private bool _BoolIsDisplayRedDiamond =&&&&&&&& //是否显示跑过的里程&&&& void Start () &&& {&&& &&& //测试数值(等待删除。)&&&&&&& //GlobalInfoManger.IntGameTime = 66;&&&&&&& //GlobalInfoManger.IntRedDiamonds = 88;&&&&&&&& //开启“动态控制显示玩家跑过的里程(时间)”&&&&&&& StartCoroutine(DynamicDisplayPlayerRunLength());&&&&&&& //开启“动态控制红宝石数量统计”&&&&&&&StartCoroutine(DynamicDisplayRedDiamondNumber());&&&& }//Start_end&&& &&&& //动态控制里程显示&&& IEnumerator DynamicDisplayPlayerRunLength()&&& {&&&&&&& //等待GUI 与摄像机全部绘制完毕。&&&&&&& yield return new WaitForEndOfFrame();&&&&&&& &&&&&&& _BoolIsDisplayRunedTime =&&&&&&& _IntRunedTime = 0;&&&&&&& yield return new WaitForSeconds(0.2F);&&&&&&& _BoolIsDisplayRunedTime =&&&&&&&& while (_IntRunedTime &GlobalInfoManger.IntGameTime)&&&&&&& {&&&&&&&&&&& //不断显示&&&&&&&&&&& ++_IntRunedT&&&&&&&&&&& _BoolIsDisplayRunedTime =&&&&&&&&&&& yield return newWaitForSeconds(0.03F);&&&&&&&&&&& _BoolIsDisplayRunedTime =&&&&&&&&&&& yield return new WaitForSeconds(0.01F);&&&&&&& }&&&&&&& _BoolIsDisplayRunedTime =&&& }&&&& //动态控制红宝石显示&&& IEnumeratorDynamicDisplayRedDiamondNumber()&&& {&&&&&&& //等待GUI 与摄像机全部绘制完毕。&&&&&&& yield return new WaitForEndOfFrame();&&&&&&&& _BoolIsDisplayRedDiamond =&&&&&&& _IntRedDiamondNum = 0;&&&&&&& yield return new WaitForSeconds(0.2F);&&&&&&& _BoolIsDisplayRedDiamond =&&&&&&&& while (_IntRedDiamondNum &GlobalInfoManger.IntRedDiamonds)&&&&&&& {&&&&&&&&&&& //不断显示&&&&&&&&&&& ++_IntRedDiamondN&&&& &&&&&&&_BoolIsDisplayRedDiamond =&&&&&&&&&&& yield return newWaitForSeconds(0.03F);&&&&&&&&&&& _BoolIsDisplayRedDiamond =&&&&&&&&&&& yield return newWaitForSeconds(0.01F);&&&&&&& }&&&&&&& _BoolIsDisplayRedDiamond =&&& }&&&& void OnGUI()&&& {&&&&&&& GUI.skin=ProjectGUIS&&&& &&& //绘制游戏背景贴图&&&&&&& GUI.DrawTexture(newRect(Screen.width/2-TexGameBackground.width/2,Screen.height/2-TexGameBackground.height/2,TexGameBackground.width,TexGameBackground.height),TexGameBackground);&&&&&&&& //显示统计玩家跑的里程&&&&&&& GUI.Label(new Rect(550, 250, 150, 200),"时间:");&&&&&&& if (_BoolIsDisplayRunedTime)&&&&&&& {&&&&&&&&&&& GUI.Label(new Rect(700, 250, 150,200), _IntRunedTime.ToString());&&&&&&& }&&&&&&&& //显示获得的红宝石的数量&&&&&&& GUI.DrawTexture(new Rect(550, 300, 50,50), TexRedDiamonds);&&&&&&& if(_BoolIsDisplayRedDiamond)&&&&&&& {&&&&&&&&&&& GUI.Label(new Rect(700, 300, 150,50),_IntRedDiamondNum.ToString());&&&&&&&&&&&&&& }&&&&&&&& //显示获得的蓝宝石的数量(测试数据)&&&&&&& GUI.DrawTexture(new Rect(550, 350, 50,50), TexBlueDiamonds);&&&&&&& GUI.Label(new Rect(700, 350, 50, 50),GlobalInfoManger.IntRedDiamonds.ToString());&&&&&&&& //显示“重玩”,"退出"。&&&&&&& if (GUI.Button(new Rect((Screen.width /2 - TexRePlay.width / 2)-100, Screen.height / 2 - TexRePlay.height / 2+150,TexRePlay.width, TexRePlay.height), "",ProjectGUISkin.GetStyle("Btn_Replay")))&&&&&&& {&&&&&&&&&&& _boolRePlay =&&&&&&& }&&&&&&& else if (GUI.Button(newRect((Screen.width / 2 - TexRePlay.width / 2)+100, Screen.height / 2 - TexRePlay.height/ 2+150, TexRePlay.width, TexRePlay.height), "",ProjectGUISkin.GetStyle("Btn_Exit")))&&&&&&& {&&&&&&&&&&& _boolExit =&&&&&&& }&&&&& }//OnGUI_end&&&& //逻辑处理。&&& void Update()&&& { &&&&&&& if(_boolRePlay)&&&&&&& {&&&&&&&&&&& //调用指定的场景。&&&&&&&&&&& GlobalInfoManger.IntGameTime =0;&&&&&&&&&&&&& //时间清零&&&&&&&&&&&Application.LoadLevel(StrScenesName);&&&&&&& }&&&&&&& else if (_boolExit)&&&&&&& {&&&&&&&&&&& //退出游戏&&&&&&&&&&& Application.Quit();&&&&&&& }&&& }&}//Class_end&&&&&&&&&&&&&&&&& &&&&&&&图 1.2&示例 1.2 参考图1.2进行理解,其基本原理与示例1.1基本相同,在这就不再赘述。 &&对于InvokeRepeating则在项目中运用的更多,请看如下示例代码。&(示例 1.3)&usingUnityEusingSystem.C&public classScenesManager_Level1 : MonoBehaviour {&&& public Transform TrnH&&&&&&&&&&&&&&&&&&&&&&&&&&&&& //主人公位置信息&&& public string StrStartScenceN&&&&&&&&&&&&&&&&&&&&& //开始场景的名称&&& //克隆道具&&& public GameObject Go_ScriptsO&&&&&&&&&&&&&&&&&&& //脚本对象&&& public GameObjectGoCloneOriginal_D&&&&&&&&&&&& //克隆原型_红宝石&&& public GameObjectGoCloneOriginal_T&&&&&&&&&&&&&&& //克隆原型_树木&&&& public GameObjectGo_CloneObj_RefPosion_L&&&&&&&&& //克隆位置基准。&&& public GameObjectGo_CloneObj_RefPosion_R&&&& private AudioSource _ASBGM&&&&&&&&&&&&&&&&&&&&&&& //背景音乐&&& private AudioSource[] _ASAudioE&&&&&&&&&&&&&&&&& //音频数组&& &private Vector3_VecHeroPositionOfO&&&&&&&&&&& //主人公原始位置&&&& void Start () &&& {&&&&&&& //游戏结束&&&&&&& GlobalInfoManger.CurrentGameState =GameState.P&&&&&&& //设置音频&&&&&&& SetAudio();&&&&&&& //主人公位置初始值&&&&&&& _VecHeroPositionOfOriginal = TrnHero.transform.&&&&&&&& //检测状态信息&&&&&&&InvokeRepeating("CheckHeroState",1F,1F);&&&&&&&& //动态生成道具。(单次加载方式)&&&&&&& //DanymicCreateRedDiamond();&&&&&&&& //分步加载方式&&&&&&&InvokeRepeating("DanymicCreateProperty",1F,1F);&&& }//Start_end&&&& /// &summary&&&& /// 动态加载各种道具&&& /// &/summary&&&& private void DanymicCreateProperty()&&& { &&&&&&& //加载红宝石&&&&&&& if(GlobalInfoManger.IntGameTime==1)&&&&&&& {&&&&&&&&&&&this.CreateProperty(GoCloneOriginal_Diamend,15,30,100);&&&&&&&&&& &&&&&&& }&&&&&&&& //第二次&&&&&&& if (GlobalInfoManger.IntGameTime ==15)&&&&&&& {&&&&&&&&&&& //加载宝石&&&&&&&&&&&this.CreateProperty(GoCloneOriginal_Diamend, 20, 30, 150);&&&&&&&& &&&&&&&&&&& //加载树木&&&&&&&&&&&this.CreateProperty(GoCloneOriginal_Tree,5, 30, 100);& &&&&&&& }&&&&&&&& //第三次。&&&&&&& if (GlobalInfoManger.IntGameTime ==20)&&&&&&& {&&&&&&&&&&& //加载地刺&&&&&&&&&&&& //加载宝石&&&&&&&&&&&this.CreateProperty(GoCloneOriginal_Diamend, 50, 30,650);&&&&&&&&&&& //加载树木&&&&&&&&&&& this.CreateProperty(GoCloneOriginal_Tree,10,30, 600);&&&&&&& }&&&& }//DanymicCreateProperty_end&&&& /// &summary&&&& /// 创建道具&&& /// &/summary&&&& /// &paramname="goCloneOriginal"&克隆原型&/param&&&& /// &paramname="intCloneNumber"&克隆数量&/param&&&& /// &paramname="intDestroyGOTime"&销毁时间&/param&&&& /// &paramname="intAddingGameobjectLenth"&克隆对象的距离&/param&&&& void CreateProperty(GameObjectgoCloneOriginal,int intCloneNumber,int intDestroyGOTime,intintAddingGameobjectLenth)&&& {&&&&&&& /* 数据集合准备 */&&&&&&& System.Object[] ObjArray = newSystem.Object[8];&&&&&&& ObjArray[0] = goCloneO&&&&&&&&&&&&&&&&&&&& //克隆的原型&&&&&&& ObjArray[1] = intCloneN&&&&&&&&&&&&&&&&&&&&& //克隆的数量&&&&&&& ObjArray[2] = intDestroyGOT&&&&&&&&&&&&&&&&&& &//克隆体销毁的时间&&&&&&& //X 坐标最小数值&&&&&&& ObjArray[3] =Go_CloneObj_RefPosion_Left.transform.position.x;&&&&&&& //X 坐标最大数值&&&&&&& ObjArray[4] =Go_CloneObj_RefPosion_Right.transform.position.x;&&&&&&& //Y坐标&&&&&&& ObjArray[5] =Go_CloneObj_RefPosion_Left.transform.position.y;&&&&&&& //Z 坐标最小数值&&&&&&& ObjArray[6] =Go_CloneObj_RefPosion_Left.transform.position.z;&&&&&&& //Z 坐标最大数值&&&&&&& ObjArray[7] =Go_CloneObj_RefPosion_Left.transform.position.z + intAddingGameobjectL&&&&&&&& /* 调用动态克隆方法 */&&&&&&&Go_ScriptsObject.SendMessage("DynamicCreProperty", ObjArray,SendMessageOptions.DontRequireReceiver);&&&&&&&& }&&&& /// &summary&&&& /// 检测主人公状态信息&&& /// &/summary&&&& void CheckHeroState()&&& {&&&&&&& //游戏时间自增&&&&&&& ++GlobalInfoManger.IntGameT&&&&&&&& //如果主人公Y轴出现较大的变化,判断主人公掉下桥体,游戏结束&&&&&&& if (TrnHero.transform.position.y &(_VecHeroPositionOfOriginal.y-10))&&&&&&&& {&&&&&&&&&&&& GlobalInfoManger.CurrentGameState= GameState.GameO&&&&&&&& }&&&&&&&& //判断游戏结束后,处理方式。&&&&&&& if (GlobalInfoManger.CurrentGameState== GameState.GameOver)&&&&&&& {&&&&&&&&&&& _ASBGMusic.Stop();&&&&&&&&&&&&&&&&&&&&&&&&&&&& //关闭背景音乐&&&&&&&&&&& _ASAudioEffect[1].Play();&&&&&&&&&&&&&&&&&&&&& //播放GameOver 音频&&&&&&&&&&& //3秒后返回开始场景&&&&&&&&&&&Invoke("ReturnStartScenes", 3F);&&&&&&& }&&& }&&&& //设置音频&&& void SetAudio()&&& {&&&&&&& //背景音乐音量大小判断&&&&&&& _ASBGMusic =GameObject.Find("_AudiosManager/_AudioBGMusic").GetComponent&AudioSource&();&&&&&&& switch (GlobalInfoManger.ProVolumns)&&&&&&& {&&&&&&&&&&& case ProjectVlumns.None:&&&&&&&&&&&&&&&&&&&&&&&&&& case ProjectVlumns.NoneVolumns:&&&&&&&&&&&&&&& _ASBGMusic.volume = 0F;&&&&&&&&&&&&&&&&&&&&&&&&&& case ProjectVlumns.HalfVolumns:&&&&&&&&&&&&&&& _ASBGMusic.volume = 0.5F;&&&&&&&&&&&&&&&&&&&&&&&&&& case ProjectVlumns.FullVolumns:&&&&&&&&&&&&&&& _ASBGMusic.volume = 1F;&&&&&&&&&&&&&&&&&&&&&&&&&& default:&&&&&&&&&&&&&&&&&&&&&& }//switch_end&& &&& &&&&&//音效处理&&&&&&& _ASAudioEffect =GameObject.Find("_AudiosManager/_AudioEffect").GetComponents&AudioSource&();&&&&&&& _ASAudioEffect[1].volume = 1F;&&&&&&& _ASAudioEffect[1].loop =&&& }&&&& //返回开始开始场景&&& void ReturnStartScenes()&&& {&&&&& &&Application.LoadLevel(StrStartScenceName);&&& }&& &}//Class_end&示例 1.3 可以参考图1.1进行理解,基本原理如下:以上在Start() 事件函数中定义的“CheckHeroState()”[检测主角的状态信息]与“DanymicCreateRedDiamond()”[动态生成道具] 方法分别都是被InvokeRepeate 进行循环调用,按照一定的固定时间间隔。 这里在本应用场景中的以上两个方法中由于不需要产生不固定的时间间隔,所以就不需要使用协程了。最后提及一下Coroutine(协程)与InvokeRepeating(重复调用函数),在被脚本禁用的时候是不会自动停止调用的,需要手工编写对应的停止调用函数进行停止,否则会出现很多问题。且还要注意调用与停止调用的时机问题,我会在下一篇博客中关于讨论Unity 脚本的生命周期中进行讨论,谢谢。(跑酷游戏)源代码下载链接:/s/1sj8X3lV本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)阅读(6823)
  1. 协程
  在Unity 3D中,我们刚开始写脚本的时候肯定会遇到类似下面这样的需求:每隔3秒发射一个烟花、怪物死亡后20秒再复活之类的。刚开始的时候喜欢把这些东西都塞到Update里面去,就像下面这样写。
1 float nowTime = 3.0f;
2 bool isDead = true;
3 float deadTime = 20.0f;
5 void startFireworks()
10 void revival()
15 void Update ()
if (nowTime &= 0)
startFireworks();
nowTime = Random.Range(2.5f, 3.5f);
nowTime -= Time.deltaT
if (isDead)
if (deadTime &= 0)
revival();
isDead = false;
deadTime = 30.0f;
deadTime -= Time.deltaT
  当这样的需求多起来时,Update中凌乱不堪,如果有需求需要添加或者修改,将显得非常麻烦。尤其是类似死亡后复活这种需求,只是在死亡后等待30秒重新复活,其他时间根本不需要去执行,这样放在Update里面还需要每一帧去判断,显得很累赘。
好在Unity 3D支持yield协程,不懂没关系,先看看下面用协程实现上面的功能。
1 void Start()
StartCoroutine(Fireworks());
6 void deadHandle()
StartCoroutine(Revival());
11 IEnumerator Fireworks()
while (true)
startFireworks();
yield return new WaitForSeconds(Random.Range(2.5f, 3.5f));
20 IEnumerator Revival()
yield return new WaitForSeconds(30.0f);
revival();
  上面代码中,以IEnumerator作为返回值的函数就是协程,调用StartCoroutine()开始协程,在Start函数中调用StartCoroutine(Fireworks());,说明在开始时就开始执行协程Fireworks(),在deadHandle()中调用StartCoroutine(Revival());说明是在怪物死亡时开始执行协程。
  现在再来看看协程Fireworks()和Revival()中带有yield return的语句,yield return new WaitForSeconds(30.0f);表示现在从return这个语句处中断执行,在30秒后继续执行后面的代码。
  如果想在下一帧继续执行,就应该这样写 yield return null,这样语句就会在return这里中断,等待下一帧继续执行后面的代码。就像在Fireworks()里面写的,return之后,继续进行while判断,为true则继续循环,遇到yield return中断执行,等待,反复这样运行,就像Update一样。当然while中的判断条件可以自己指定,在需要中断的时候,在外面将while中的判断条件置为false即可。
1 bool isContinue = true;
2 void stopFireworks()
isContinue = false;
6 IEnumerator Fireworks()
while (isContinue)
startFireworks();
yield return new WaitForSeconds(Random.Range(2.5f, 3.5f));
  当然也可以通过有StopCoroutine来中止协程的执行,不过这个函数是有条件的,具体可以去查阅unity文档或者网上搜索一下,有很多资料,这里只是告诉大家有这么个东西可以用。
  有了协程,写起脚本来真是方便了很多。协程和Update一样,也是系统在每帧会去检测调用,因此在协程中也是可以使用Time.deltaTime的。关于协程与Update之类的执行顺序,没有测试过,网上也有一些资料,大家可以参考,不同的Unity 3D版本具体的实现可能有出入,如果某些功能确实需要知道执行顺序,那么到时候可以亲测一下。
  需要注意的一点是:WaitForSeconds是受到Time.timeScale影响的,如果将其置为0,那么协程就无法执行下去了。不过yield return null不会受到影响,因为每帧会执行,只是Time.deltaTime为0了。
  2. 消息传递
  在游戏开发中,消息传递必不可少。通常有三种方式:保存别的对象的引用、Unity自带的SendMessage和C#中的事件。
  例如一个暂停,我需要通知玩家,暂停了,不要响应键盘鼠标操作了;通知UI,显示一个暂停面板;通过所有怪物,不要动了,暂停了,休息一下。&
  第一种方式是刚开始写脚本时常用的,保存所有对象的引用,这是很麻烦的事情,我需要获取玩家、UI和所有的怪物对象,然后调用其相应的暂停函数,这在程序规模变大之后,添加、修改和删除是一个很大的工作量。而且很多对象之间相互引用,耦合对也很高,用起来比较麻烦。
  第二种方式是Unity 3D提供的Messages消息机制,不过网上说这种方法有很大的缺陷,而且只能通知一个父子关系的对象,不同对象之间的消息无法传递。没有用过这个机制,所以也不是很清楚是不是像上面说的那样。
  第三种方式是C#中的委托和事件,这个方法对于消息传递来说非常好用,从设计模式的角度上来说,就是一个典型的观察者模式。如果你用过EasyTouch摇杆,那你就应该知道在OnEnable()中使用EasyJoystick.On_JoystickMove += OnJoystickM注册自己的Move函数,在这里就是OnJoystickMove。其实EasyTouch这个就用到了C#事件,使用+=添加自己的响应函数,当发生摇杆移动时,就会调用你自己指定的OnJoystickMove函数。具体可以参考下面给出的参考资料的链接。
  今天就写到这里,这些都是简介性质的,详细资料网上都有很多,我这些只是告诉初学者Unity 3D中有这些东西,很可能是你需要的,可以少走一些弯路。
  关于协程和C#事件,是Unity 3D中强力推荐的两个机制,它们真的非常重要,一定要善用,大家可以体会一下。
  参考资料1:
  参考资料2:
&原文链接:
阅读排行榜

我要回帖

更多关于 unity 停止协程 的文章

 

随机推荐