猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!
原文地址: http://www.cocos2dev.com/?p=496
Coroutine在Unity3D中叫做协程或协同程序,和多线程类似,也就是说开启协同程序就是开启一个线程。但是在任意指定时刻只有一个协程执行,其他协程挂起。
Coroutine的相关函数:
StartCoroutine:启动一个协程。
StopCoroutine:终止一个协程。
StopAllCoroutine:终止所有协程。
WaitForSeconds:等待几秒。
WaitForFixedUpdate:等到下一次FixedUpdate调用时执行。
使用MonoBehaviour.StartCoroutine方法即可开启一个协同程序,也就是说该方法必须在 MonoBehaviour 或继承于MonoBehaviour的类中调用。
(下面一段是引用与网络上教程,因为讲的很详细了,所以这里直接引用下。为找到原始出处)
注意:在Unity3D中,
StartCoroutine(string methodName)
StartCoroutine(IEnumerator routine)
都可以开启一个线程。
区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);
另外使用字符串作为参数时,开启线程时最多只能传递 一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。
在Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该 MonoBehaviour中的协同程序。
还有一种方法可以终止协同程序,即将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程序并不会再开启;
如是将协同程序所在脚本的enabled设置为false则不会生效。这是因为协同程序被开启后作为一个线程在运行,而 MonoBehaviour也是一个线程,他们成为互不干扰的模块,除非代码中有调用,他们共同作用于同一个对象,只有当对象不可见才能同时终止这两个线 程。
然而,为了管理我们额外开启的线程,Unity3D将协同程序的调用放在了MonoBehaviour中,这样我们在编程时就可以方便的调用指定脚本 中的协同程序,而不是无法去管理,特别是对于只根据方法名来判断线程的方式在多人开发中很容易出错,这样的设计保证了对象、脚本的条理化管理,并防止了重 名。
注意:在GameObject.active = false 时他会销毁你的任何一个协同程序过程,并且不会等待过程结束后销毁,GameObject.enable时他并不可以恢复。
协同程序的返回类型为Coroutine类型。在Unity3D中,Coroutine类继承于YieldInstruction,所以,协同程序的返回类型只能为null、等待的帧数(frame)以及等待的时间。
准备写个例子,但是MOMO的这个已经非常详细了,而且我的是基础版,所以这里就直接把MOMO这篇放过来了。(点击查看原文地址)
异步任务相信大家应该不会陌生,那么本章内容MOMO将带领大家学习Unity中的一些异步任务。在同步加载游戏场景的时候通常会使用方法 Application.LoadLevel(“yourScene”); 这句代码执行完毕后程序会干什么呢??如下图所示,这是我随便找了一个游戏场景, 在Hierarchy视图中我们可以看到该场景中“天生”的所有游戏对象。天生的意思就是运行程序前该场景中就已经存在的所有游戏对象。然后这些对象就会在执行完Application.LoadLevel(“yourScene”);方法后加载至内存当中。如果该场景中的游戏对象过多那么瞬间将会出现卡一下的情况,因为LoadLevel()方法是同步进行的。MOMO把这种加载起个名字叫A形式加载。
下面我说说“后天“加载的游戏对象。意思是这些游戏对象是通过脚本动态的创建出来的。比如常用方法 :
GameObject Obj = (GameObject)Instantiate(prefab);
Application.LoadLevelAsync("yourScene");//only pro
Application.LoadLevelAdditiveAsync ("yourScene");
using UnityEngine; using System.Collections; public class Globe { //在这里记录当前切换场景的名称 public static string loadName; }
在A场景中通过某些触发条件 调用LoadLevel进入B场景。
//记录LOADING场景中需要读取的C场景名称 Globe.loadName = "C"; //先进入B场景 Application.LoadLevel ("B");
OK我们在B场景中异步读取C场景与 播放读取动画,Loading.cs 绑定在B场景的摄像机对象身上。当C场景异步读取完毕后即可直接进入C场景。
using UnityEngine; using System.Collections; public class Loading : MonoBehaviour { private float fps = 10.0f; private float time; //一组动画的贴图,在编辑器中赋值。 public Texture2D[] animations; private int nowFram; //异步对象 AsyncOperation async; //读取场景的进度,它的取值范围在0 - 1 之间。 int progress = 0; void Start() { //在这里开启一个异步任务, //进入loadScene方法。 StartCoroutine(loadScene()); } //注意这里返回值一定是 IEnumerator IEnumerator loadScene() { //异步读取场景。 //Globe.loadName 就是A场景中需要读取的C场景名称。 async = Application.LoadLevelAsync(Globe.loadName); //读取完毕后返回, 系统会自动进入C场景 yield return async; } void OnGUI() { //因为在异步读取场景, //所以这里我们可以刷新UI DrawAnimation(animations); } void Update() { //在这里计算读取的进度, //progress 的取值范围在0.1 - 1之间, 但是它不会等于1 //也就是说progress可能是0.9的时候就直接进入新场景了 //所以在写进度条的时候需要注意一下。 //为了计算百分比 所以直接乘以100即可 progress = (int)(async.progress *100); //有了读取进度的数值,大家可以自行制作进度条啦。 Debug.Log("xuanyusong" +progress); } //这是一个简单绘制2D动画的方法,没什么好说的。 void DrawAnimation(Texture2D[] tex) { time += Time.deltaTime; if(time >= 1.0 / fps){ nowFram++; time = 0; if(nowFram >= tex.Length) { nowFram = 0; } } GUI.DrawTexture(new Rect( 100,100,40,60) ,tex[nowFram] ); //在这里显示读取的进度。 GUI.Label(new Rect( 100,180,300,60), "lOADING!!!!!" + progress); } }
OK 下面我们继续学习在游戏场景中加载对象,文章的开始MOMO已经告诉大家,游戏场景中Hierarchy视图中的所有的对象在切换场景的时候都会加载。其实有一种方法可以让某些游戏对象不会被加载,如下图所示,首先在Hierarchy视图中选择一个游戏对象,在右侧监测面板视图中我们可以看到一个 “小对勾”默认情况下是勾选状态,说明该游戏对象处于激活状态,如果点掉的话该对象将被隐藏。这个小功能在开发中其实用处非常大,请大家务必记住哈。
此时此刻大家相像一个游戏场景,默认进入的时候是没有任何游戏对象的,然后运行游戏时开启一个异步任务将它们一个一个的加载显示出来,这种方式适合异步的加载一个比较大的游戏场景。
Test.cs 把它挂在摄像机对象中。
using UnityEngine; using System.Collections; public class Test : MonoBehaviour { //这里是需要加载激活的游戏对象 public GameObject [] Objects; //当前加载的进度 int load_index =0; void Start () { //开启一个异步任务,加载模型。 StartCoroutine(loadObject()); } IEnumerator loadObject() { //便利所有游戏对象 foreach(GameObject obj in Objects) { //激活游戏对象 obj.active = true; //记录当前加载的对象 load_index ++; //这里可以理解为通知主线程刷新UI yield return 0; } //全部便利完毕返回 yield return 0; } void OnGUI () { //显示加载的进度 GUILayout.Box("当前加载的对象ID是: " + load_index); } }
当然我们还可以使用Instantiate(prefab);方法来动态的创建游戏对象。
Main.cs 把它挂在摄像机中。
using UnityEngine; using System.Collections; public class Main : MonoBehaviour { public int count; //在编辑器中预设一个游戏对象 public GameObject prefab; void Start () { StartCoroutine(loaditem()); } void OnGUI() { GUILayout.Box("游戏对象已经加载到 : " + count); } IEnumerator loaditem() { //开始加载游戏对象 for(int i =0; i< 1000; i++) { Instantiate(prefab); count = i; //可以理解为刷新UI,显示新加载的游戏对象 yield return 0; } //结束 yield return 0; } }
ok, 这个介绍是MOMO写的,我的第一本Unity书也是买的MOMO的。