如何在Unity中自己写Coroutine协程源码
无意之间看到了,Unity维基上的一篇文章,是关于自己写协程的介绍。觉得很有价值,因为这样大家能更好的了解到协程的运行机制等特性,还是不错的,所以就将如何在Unity中自己写Coroutine协程源码分享给大家。
原文链接地址如下:
http://wiki.unity3d.com/index.php?title=CoroutineScheduler
项目地址: http://download.csdn.net/detail/u010019717/8912069
具体的内容如下:
一个简单的协同调度程序。这个协同调度程序允许完全控制一套协同程序的执行机制。 阅读代码也将帮助您了解 协同如何在幕后工作。了解协同程序如何构建.Net 发电机的基础上构建,将允许您将协同支持添加到非Unity的项目。
协同可以yield等待 直到下次更新"yield;", 直到给定的数量的更新已通过 "yield anInt;", 直到给定的秒已通过 "yield aFloat;", 或者直到另一个协程已完成"yield scheduler.StartCoroutine(Coroutine());".StartCoroutine(Coroutine());"。
多个 调度 程序实例支持,并且可以非常有用。协同运行可以运行在一个完全不同的 调度程序实例下 (等待)。
不使用Unity的 YieldInstruction 类,因为我门不能访问这个类所需的调度的内部数据。 语义学Semantics 是和Unity的调度程序略有不同。 例如,在 Unity 中如果你开始协同 它将对其第一次的 yield 立即运行,然而 在自己写的调度程序中,它将不会运行,直到下一次调用 UpdateAllCoroutines。 此功能允许在任何时候的任何代码开始启动一个协程。 同时确保启动协同程序只能运行在特定的时间。
在相同的update协同程序运行之间不应该依赖 更新order。
为更深入地了解和学会更多关于 协同程序 如何实现。
使用:
- using UnityEngine;
- using System.Collections;
- /// <summary>
- /// CoroutineSchedulerTest.cs
- ///
- /// Port of the Javascript version from
- /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler
- ///
- /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.
- ///
- /// BMBF Researchproject http://playfm.htw-berlin.de
- /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management
- /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH
- ///
- /// <author>Frank.Otto@htw-berlin.de</author>
- ///
- /// </summary>
- public class testAPI : MonoBehaviour {
- CoroutineScheduler scheduler;
- CoroutineScheduler m_scheduler2;
- string requestURL = "http://www.my-server.com/cgi-bin/screenshot.pl";
- void Start () {
- scheduler = new CoroutineScheduler ();
- scheduler.StartCoroutine (MyCoroutine ());
- m_scheduler2 = new CoroutineScheduler();
- m_scheduler2.StartCoroutine(test());
- }
- IEnumerator MyCoroutine ()
- {
- Debug.Log ("MyCoroutine: Begin");
- yield return 0;
- // wait for next update
- Debug.Log ("MyCoroutine: next update;" Time.time);
- yield return 2;
- // wait for 2 updates, same as yield; yield;
- Debug.Log ("MyCoroutine: After yield 2;" Time.time);
- yield return 3.5f;
- // wait for 3.5 seconds
- Debug.Log ("MyCoroutine: After 3.5 seconds;" Time.time);
- // you can also yield for a coroutine running on a completely different scheduler instance
- yield return scheduler.StartCoroutine (WaitForMe ());
- Debug.Log ("MyCoroutine: After WaitForMe() finished;" Time.time);
- }
- IEnumerator WaitForMe ()
- {
- yield return 7.8f;
- // wait for 7.8 seconds before finishing
- }
- // Update is called once per
- void Update ()
- {
- scheduler.UpdateAllCoroutines (Time.frameCount, Time.time);
- }
- IEnumerator test()
- {
- // ...set up request
- var www = new UnityEngine.WWW(requestURL);
- yield return new UnityWWWYieldWrapper(www);
- // ...loading complete do some stuff
- }
- }
CoroutineScheduler.cs
- using System.Collections;
- using UnityEngine;
- /// <summary>
- /// CoroutineScheduler.cs
- ///
- /// Port of the Javascript version from
- /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler
- ///
- /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.
- ///
- ///
- /// BMBF Researchproject http://playfm.htw-berlin.de
- /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management
- /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH
- ///
- /// <author>Frank.Otto@htw-berlin.de</author>
- ///
- ///
- /// A simple coroutine scheduler. Coroutines can yield until the next update
- /// "yield;", until a given number of updates "yield anInt", until a given
- /// amount of seconds "yield aFloat;", or until another coroutine has finished
- /// "yield scheduler.StartCoroutine(Coroutine())".
- ///
- /// Multiple scheduler instances are supported and can be very useful. A
- /// coroutine running under one scheduler can yield (wait) for a coroutine
- /// running under a completely different scheduler instance.
- ///
- /// Unity's YieldInstruction classes are not used because I cannot
- /// access their internal data needed for scheduling. Semantics are slightly
- /// different from Unity's scheduler. For example, in Unity if you start a
- /// coroutine it will run up to its first yield immediately, while in this
- /// scheduler it will not run until the next time UpdateAllCoroutines is called.
- /// This feature allows any code to start coroutines at any time, while
- /// making sure the started coroutines only run at specific times.
- ///
- /// You should not depend on update order between coroutines running on the same
- /// update. For example, StartCoroutine(A), StartCoroutine(B), StartCoroutine(C)
- /// where A, B, C => while(true) { print(A|B|C); yield; }, do not expect "ABC" or
- /// "CBA" or any other specific ordering.
- /// </summary>
- public class CoroutineScheduler : MonoBehaviour
- {
- CoroutineNode first = null;
- int currentFrame;
- float currentTime;
- /**
- * Starts a coroutine, the coroutine does not run immediately but on the
- * next call to UpdateAllCoroutines. The execution of a coroutine can
- * be paused at any point using the yield statement. The yield return value
- * specifies when the coroutine is resumed.
- */
- public CoroutineNode StartCoroutine (IEnumerator fiber)
- {
- // if function does not have a yield, fiber will be null and we no-op
- if (fiber == null) {
- return null;
- }
- // create coroutine node and run until we reach first yield
- CoroutineNode coroutine = new CoroutineNode (fiber);
- AddCoroutine (coroutine);
- return coroutine;
- }
- /**
- * Stops all coroutines running on this behaviour. Use of this method is
- * discouraged, think of a natural way for your coroutines to finish
- * on their own instead of being forcefully stopped before they finish.
- * If you need finer control over stopping coroutines you can use multiple
- * schedulers.
- */
- public void StopAllCoroutines ()
- {
- first = null;
- }
- /**
- * Returns true if this scheduler has any coroutines. You can use this to
- * check if all coroutines have finished or been stopped.
- */
- public bool HasCoroutines ()
- {
- return first != null;
- }
- /**
- * Runs all active coroutines until their next yield. Caller must provide
- * the current frame and time. This allows for schedulers to run under
- * frame and time regimes other than the Unity's main game loop.
- */
- public void UpdateAllCoroutines(int frame, float time)
- {
- currentFrame = frame;
- currentTime = time;
- CoroutineNode coroutine = this.first;
- while (coroutine != null)
- {
- // store listNext before coroutine finishes and is removed from the list
- CoroutineNode listNext = coroutine.listNext;
- if (coroutine.waitForFrame > 0 && frame >= coroutine.waitForFrame)
- {
- coroutine.waitForFrame = -1;
- UpdateCoroutine(coroutine);
- }
- else if (coroutine.waitForTime > 0.0f && time >= coroutine.waitForTime)
- {
- coroutine.waitForTime = -1.0f;
- UpdateCoroutine(coroutine);
- }
- else if (coroutine.waitForCoroutine != null && coroutine.waitForCoroutine.finished)
- {
- coroutine.waitForCoroutine = null;
- UpdateCoroutine(coroutine);
- }
- else if (coroutine.waitForUnityObject != null && coroutine.waitForUnityObject.finished)//lonewolfwilliams
- {
- coroutine.waitForUnityObject = null;
- UpdateCoroutine(coroutine);
- }
- else if (coroutine.waitForFrame == -1 && coroutine.waitForTime == -1.0f
- && coroutine.waitForCoroutine == null && coroutine.waitForUnityObject == null)
- {
- // initial update
- UpdateCoroutine(coroutine);
- }
- coroutine = listNext;
- }
- }
- /**
- * Executes coroutine until next yield. If coroutine has finished, flags
- * it as finished and removes it from scheduler list.
- */
- private void UpdateCoroutine(CoroutineNode coroutine)
- {
- IEnumerator fiber = coroutine.fiber;
- if (coroutine.fiber.MoveNext())
- {
- System.Object yieldCommand = fiber.Current == null ? (System.Object)1 : fiber.Current;
- if (yieldCommand.GetType() == typeof(int))
- {
- coroutine.waitForFrame = (int)yieldCommand;
- coroutine.waitForFrame = (int)currentFrame;
- }
- else if (yieldCommand.GetType() == typeof(float))
- {
- coroutine.waitForTime = (float)yieldCommand;
- coroutine.waitForTime = (float)currentTime;
- }
- else if (yieldCommand.GetType() == typeof(CoroutineNode))
- {
- coroutine.waitForCoroutine = (CoroutineNode)yieldCommand;
- }
- else if (yieldCommand is IYieldWrapper) //lonewolfwilliams
- {
- coroutine.waitForUnityObject = yieldCommand as IYieldWrapper;
- }
- else
- {
- throw new System.ArgumentException("CoroutineScheduler: Unexpected coroutine yield type: " yieldCommand.GetType());
- //this is an alternative if you don't have access to the function passed to the couroutineScheduler - maybe it's
- //precompiled in a dll for example - remember you will have to add a case every time you add a wrapper :/
- /*
- var commandType = yieldCommand.GetType();
- if(commandType == typeof(UnityEngine.WWW))
- {
- coroutine.waitForUnityObject =
- new UnityWWWWrapper(yieldCommand as UnityEngine.WWW);
- }
- else if(commandType == typeof(UnityEngine.AsyncOperation))
- {
- coroutine.waitForUnityObject =
- new UnityASyncOpWrapper(yieldCommand as UnityEngine.AsyncOperation);
- }
- else if(commandType == typeof(UnityEngine.AssetBundleRequest))
- {
- coroutine.waitForUnityObject =
- new UnityAssetBundleRequestWrapper(yieldCommand as UnityEngine.AssetBundleRequest);
- }
- else
- {
- throw new System.ArgumentException("CoroutineScheduler: Unexpected coroutine yield type: " yieldCommand.GetType());
- }
- */
- }
- }
- else
- {
- // coroutine finished
- coroutine.finished = true;
- RemoveCoroutine(coroutine);
- }
- }
- private void AddCoroutine (CoroutineNode coroutine)
- {
- if (this.first != null) {
- coroutine.listNext = this.first;
- first.listPrevious = coroutine;
- }
- first = coroutine;
- }
- private void RemoveCoroutine (CoroutineNode coroutine)
- {
- if (this.first == coroutine) {
- // remove first
- this.first = coroutine.listNext;
- } else {
- // not head of list
- if (coroutine.listNext != null) {
- // remove between
- coroutine.listPrevious.listNext = coroutine.listNext;
- coroutine.listNext.listPrevious = coroutine.listPrevious;
- } else if (coroutine.listPrevious != null) {
- // and listNext is null
- coroutine.listPrevious.listNext = null;
- // remove last
- }
- }
- coroutine.listPrevious = null;
- coroutine.listNext = null;
- }
- }//class
CoroutineNode.cs
- using System.Collections;
- using UnityEngine;
- /// <summary>
- /// CoroutineNode.cs
- ///
- /// Port of the Javascript version from
- /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler
- ///
- /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.
- ///
- /// BMBF Researchproject http://playfm.htw-berlin.de
- /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management
- /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH
- ///
- /// <author>Frank.Otto@htw-berlin.de</author>
- ///
- /// </summary>
- public class CoroutineNode
- {
- public CoroutineNode listPrevious = null;
- public CoroutineNode listNext = null;
- public IEnumerator fiber;
- public bool finished = false;
- public int waitForFrame = -1;
- public float waitForTime = -1.0f;
- public CoroutineNode waitForCoroutine;
- public IYieldWrapper waitForUnityObject; //lonewolfwilliams
- public CoroutineNode(IEnumerator _fiber)
- {
- this.fiber = _fiber;
- }
- }
IYieldWrapper.cs
- /*
- * gareth williams
- * http://www.lonewolfwilliams.com
- */
- public interface IYieldWrapper
- {
- bool finished { get; }
- }
Example Wrappers
Below are some examples of wrappers I have used, in fact they have almost identical signatures so a more generic implementation could probably be written ^_^
UnityASyncOpWrapper
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- /*
- * Gareth Williams
- * http://www.lonewolfwilliams.com
- */
- class UnityASyncOpWrapper : IYieldWrapper
- {
- private UnityEngine.AsyncOperation m_UnityObject;
- public bool finished
- {
- get
- {
- return m_UnityObject.isDone;
- }
- }
- public UnityASyncOpWrapper(UnityEngine.AsyncOperation wraps)
- {
- m_UnityObject = wraps;
- }
- }
UnityWWWYieldWrapper
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- /*
- * Gareth Williams
- * http://www.lonewolfwilliams.com
- */
- public class UnityWWWYieldWrapper : IYieldWrapper
- {
- private UnityEngine.WWW m_UnityObject;
- public bool finished
- {
- get
- {
- return m_UnityObject.isDone;
- }
- }
- public UnityWWWYieldWrapper(UnityEngine.WWW wraps)
- {
- m_UnityObject = wraps;
- }
- }
如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引