由于Unity 3D是单线程的,因此要想实现一些类似于多线程的功能,Unity实现了协程机制,要明确协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。
协程的定义
IEnumerator test1(float waitTime) {//可变参数 yield return null;//yield return表示协程暂停,将控制权交给Unity3D }
开启协程
public Coroutine StartCoroutine(IEnumerator routine); public Coroutine StartCoroutine(string methodName, [DefaultValue("null")] object value);
停止协程
public void StopCoroutine(string methodName); public void StopCoroutine(IEnumerator routine); //前者是使用方法名字符串,后者是使用方法的引用。 //前者可以停止第一个名字为methodName的协程;后者可以准确地停止你引用的那个协程 //第一种 StartCoroutine("DoSomething"); yield return new WaitForSeconds(2f); StopCoroutine("DoSomething"); //第二种 IEnumerator dosomething = DoSomething(); StartCoroutine(dosomething); yield return new WaitForSeconds(2f); StopCoroutine(dosomething); //错误示例:并不能停止DoSomething,开启的协程和停止的协程不是同一个引用 StartCoroutine(DoSomething()); yield return new WaitForSeconds(2f); StopCoroutine(DoSomething());
延时功能
IEnumerator test2(float waitTime) { //等待waitTime秒之后执行后续代码 yield return new WaitForSeconds(waitTime); //暂停协程直到下一次FixedUpdate时才会继续执行协程,WaitForFixedUpdate类暂停的时间取决于Unity3D的编辑器中的 TimeManager的FixedTimestep中的值 yield return new WaitForFixedUpdate(); //等到所有摄像机和GUI被渲染完成后,再恢复协程的执行 yield return new WaitForEndOfFrame(); }
使用WaitForEndOfFrame()延时截取当前屏幕的画面
IEnumerator ScreenShotPNG() { yield return new WaitForEndOfFrame(); int width = Screen.width; int height = Screen.height; Texture2D tex = new Texture2D(width,height,TextureFormat.RGB24,false); tex.ReadPixels(new Rect(0, 0, width, height), 0, 0); tex.Apply(); byte[] bytes = tex.EncodeToPNG(); Destroy(tex); File.WriteAllBytes(Application.dataPath + "/../SaveScreen.png", bytes); }
协程背后的迭代器原理
C#代码:
void Main(){ IEnumerable<int> enumerable = TestStateChange(); IEnumerator enumerator = enumerable.GetEnumerator();//此时迭代器状态由-2变为0 bool hasNext = enumerator.MoveNext();//迭代器开始Running,迭代器状态由0变为-1 Console.WriteLine("第一次调用MoveNext, 是否有数据" + hasNext); hasNext = enumerator.MoveNext(); Console.WriteLine("第二次调用MoveNext, 是否有数据" + hasNext); hasNext = enumerator.MoveNext(); Console.WriteLine("第三次调用MoveNext, 是否有数据" + hasNext); } IEnumerable<int> TestStateChange() { Console.WriteLine("----我TestStateChange是第一行代码"); Console.WriteLine("----我是第一个yield return前的代码"); yield return 1; Console.WriteLine("----我是第一个yield return后的代码"); Console.WriteLine("----我是第二个yield return前的代码"); yield return 2; Console.WriteLine("----我是第二个yield return后的代码"); }
迭代器内部状态机的状态切换。
-2状态:只有IEnumerable才有,表明在第一次调用GetEnumerator之前的状态
-1状态:C#语言标准中规定的Running状态,表明此迭代器正在执行,当然,也会用于After状态
0状态:Before状态,表明MoveNext()还一次都没有调用过
WWW和协程
public class HttpWrapper : MonoBehaviour { public void GET(string url, Action<WWW> onSuccess, Action<WWW> onFail = null) { WWW www = new WWW(url); StartCoroutine(WaitForResponse(www, onSuccess, onFail)); } public void POST(string url, Dictionary<string, string> post, Action<WWW> onSuccess, Action<WWW> onFail = null) { WWWForm form = new WWWForm(); foreach (KeyValuePair<string, string> arg in post) { form.AddField(arg.Key, arg.Value); } WWW www = new WWW(url, form); StartCoroutine(WaitForResponse(www, onSuccess, onFail)); } IEnumerator WaitForResponse(WWW www, Action<WWW> onSuccess, Action<WWW> onFail = null) { yield return www; if (www.error == null) { onSuccess(www); } else { if (onFail != null) { onFail(www); } } } }