如何在Unity中自己写Coroutine协程源码

发表于2017-10-16
评论0 943浏览

无意之间看到了,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。

为更深入地了解和学会更多关于 协同程序 如何实现。 

使用:

  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4.   
  5. /// <summary>  
  6. /// CoroutineSchedulerTest.cs  
  7. ///   
  8. /// Port of the Javascript version from   
  9. /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler  
  10. ///   
  11. /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.  
  12. ///    
  13. /// BMBF Researchproject http://playfm.htw-berlin.de  
  14. /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management   
  15. /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH  
  16. ///   
  17. /// <author>Frank.Otto@htw-berlin.de</author>  
  18. ///  
  19. /// </summary>  
  20.   
  21. public class testAPI : MonoBehaviour {  
  22.     CoroutineScheduler scheduler;  
  23.     CoroutineScheduler m_scheduler2;  
  24.     string requestURL = "http://www.my-server.com/cgi-bin/screenshot.pl";  
  25.   
  26.     void Start () {  
  27.         scheduler = new CoroutineScheduler ();  
  28.         scheduler.StartCoroutine (MyCoroutine ());  
  29.   
  30.         m_scheduler2 = new CoroutineScheduler();  
  31.         m_scheduler2.StartCoroutine(test());  
  32.     }  
  33.   
  34.     IEnumerator MyCoroutine ()  
  35.     {  
  36.         Debug.Log ("MyCoroutine: Begin");  
  37.         yield return 0;  
  38.         // wait for next update  
  39.         Debug.Log ("MyCoroutine: next update;"   Time.time);  
  40.         yield return 2;  
  41.         // wait for 2 updates, same as yield; yield;  
  42.         Debug.Log ("MyCoroutine: After yield 2;"   Time.time);  
  43.         yield return 3.5f;  
  44.         // wait for 3.5 seconds  
  45.         Debug.Log ("MyCoroutine: After 3.5 seconds;"   Time.time);  
  46.         // you can also yield for a coroutine running on a completely different scheduler instance  
  47.         yield return scheduler.StartCoroutine (WaitForMe ());  
  48.         Debug.Log ("MyCoroutine: After WaitForMe() finished;"   Time.time);  
  49.     }  
  50.       
  51.     IEnumerator WaitForMe ()  
  52.     {  
  53.         yield return 7.8f;  
  54.         // wait for 7.8 seconds before finishing  
  55.     }  
  56.       
  57.     // Update is called once per      
  58.     void Update ()  
  59.     {  
  60.           
  61.         scheduler.UpdateAllCoroutines (Time.frameCount, Time.time);  
  62.     }  
  63.   
  64.       
  65.     IEnumerator test()  
  66.     {  
  67.         // ...set up request  
  68.           
  69.         var www = new UnityEngine.WWW(requestURL);  
  70.         yield return new UnityWWWYieldWrapper(www);  
  71.           
  72.         // ...loading complete do some stuff  
  73.     }  
  74. }  

CoroutineScheduler.cs

  1. using System.Collections;  
  2. using UnityEngine;  
  3.   
  4. /// <summary>  
  5. /// CoroutineScheduler.cs  
  6. ///   
  7. /// Port of the Javascript version from   
  8. /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler  
  9. ///   
  10. /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.  
  11. ///   
  12. ///   
  13. /// BMBF Researchproject http://playfm.htw-berlin.de  
  14. /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management   
  15. /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH  
  16. ///   
  17. /// <author>Frank.Otto@htw-berlin.de</author>  
  18. ///  
  19. ///   
  20. /// A simple coroutine scheduler. Coroutines can yield until the next update  
  21. /// "yield;", until a given number of updates "yield anInt", until a given  
  22. /// amount of seconds "yield aFloat;", or until another coroutine has finished  
  23. /// "yield scheduler.StartCoroutine(Coroutine())".  
  24. ///   
  25. /// Multiple scheduler instances are supported and can be very useful. A  
  26. /// coroutine running under one scheduler can yield (wait) for a coroutine  
  27. /// running under a completely different scheduler instance.  
  28. ///   
  29. /// Unity's YieldInstruction classes are not used because I cannot  
  30. /// access their internal data needed for scheduling. Semantics are slightly  
  31. /// different from Unity's scheduler. For example, in Unity if you start a  
  32. /// coroutine it will run up to its first yield immediately, while in this  
  33. /// scheduler it will not run until the next time UpdateAllCoroutines is called.  
  34. /// This feature allows any code to start coroutines at any time, while  
  35. /// making sure the started coroutines only run at specific times.  
  36. ///   
  37. /// You should not depend on update order between coroutines running on the same  
  38. /// update. For example, StartCoroutine(A), StartCoroutine(B), StartCoroutine(C)  
  39. /// where A, B, C => while(true) { print(A|B|C); yield; }, do not expect "ABC" or  
  40. /// "CBA" or any other specific ordering.  
  41. /// </summary>  
  42. public class CoroutineScheduler : MonoBehaviour  
  43. {  
  44.       
  45.     CoroutineNode first = null;  
  46.     int currentFrame;  
  47.     float currentTime;  
  48.       
  49.     /** 
  50.    * Starts a coroutine, the coroutine does not run immediately but on the 
  51.    * next call to UpdateAllCoroutines. The execution of a coroutine can 
  52.    * be paused at any point using the yield statement. The yield return value 
  53.    * specifies when the coroutine is resumed. 
  54.    */  
  55.       
  56.     public CoroutineNode StartCoroutine (IEnumerator fiber)  
  57.     {  
  58.         // if function does not have a yield, fiber will be null and we no-op  
  59.         if (fiber == null) {  
  60.             return null;  
  61.         }  
  62.         // create coroutine node and run until we reach first yield  
  63.         CoroutineNode coroutine = new CoroutineNode (fiber);  
  64.         AddCoroutine (coroutine);  
  65.         return coroutine;  
  66.     }  
  67.       
  68.     /** 
  69.    * Stops all coroutines running on this behaviour. Use of this method is 
  70.    * discouraged, think of a natural way for your coroutines to finish 
  71.    * on their own instead of being forcefully stopped before they finish. 
  72.    * If you need finer control over stopping coroutines you can use multiple 
  73.    * schedulers. 
  74.    */  
  75.     public void StopAllCoroutines ()  
  76.     {  
  77.         first = null;  
  78.     }  
  79.       
  80.     /** 
  81.    * Returns true if this scheduler has any coroutines. You can use this to 
  82.    * check if all coroutines have finished or been stopped. 
  83.    */  
  84.     public bool HasCoroutines ()  
  85.     {  
  86.         return first != null;  
  87.     }  
  88.       
  89.     /** 
  90.    * Runs all active coroutines until their next yield. Caller must provide 
  91.    * the current frame and time. This allows for schedulers to run under 
  92.    * frame and time regimes other than the Unity's main game loop. 
  93.    */  
  94.     public void UpdateAllCoroutines(int frame, float time)  
  95.     {  
  96.         currentFrame = frame;  
  97.         currentTime = time;  
  98.         CoroutineNode coroutine = this.first;  
  99.         while (coroutine != null)  
  100.         {  
  101.             // store listNext before coroutine finishes and is removed from the list  
  102.             CoroutineNode listNext = coroutine.listNext;  
  103.               
  104.             if (coroutine.waitForFrame > 0 && frame >= coroutine.waitForFrame)  
  105.             {  
  106.                 coroutine.waitForFrame = -1;  
  107.                 UpdateCoroutine(coroutine);  
  108.             }  
  109.             else if (coroutine.waitForTime > 0.0f && time >= coroutine.waitForTime)  
  110.             {  
  111.                 coroutine.waitForTime = -1.0f;  
  112.                 UpdateCoroutine(coroutine);  
  113.             }  
  114.             else if (coroutine.waitForCoroutine != null && coroutine.waitForCoroutine.finished)  
  115.             {  
  116.                 coroutine.waitForCoroutine = null;  
  117.                 UpdateCoroutine(coroutine);  
  118.             }  
  119.             else if (coroutine.waitForUnityObject != null && coroutine.waitForUnityObject.finished)//lonewolfwilliams  
  120.             {  
  121.                 coroutine.waitForUnityObject = null;  
  122.                 UpdateCoroutine(coroutine);  
  123.             }  
  124.             else if (coroutine.waitForFrame == -1 && coroutine.waitForTime == -1.0f   
  125.                      && coroutine.waitForCoroutine == null && coroutine.waitForUnityObject == null)  
  126.             {  
  127.                 // initial update  
  128.                 UpdateCoroutine(coroutine);  
  129.             }  
  130.             coroutine = listNext;  
  131.         }  
  132.     }  
  133.       
  134.     /** 
  135.    * Executes coroutine until next yield. If coroutine has finished, flags 
  136.    * it as finished and removes it from scheduler list. 
  137.    */  
  138.     private void UpdateCoroutine(CoroutineNode coroutine)  
  139.     {  
  140.         IEnumerator fiber = coroutine.fiber;  
  141.         if (coroutine.fiber.MoveNext())  
  142.         {  
  143.             System.Object yieldCommand = fiber.Current == null ? (System.Object)1 : fiber.Current;  
  144.               
  145.             if (yieldCommand.GetType() == typeof(int))  
  146.             {  
  147.                 coroutine.waitForFrame = (int)yieldCommand;  
  148.                 coroutine.waitForFrame  = (int)currentFrame;  
  149.             }  
  150.             else if (yieldCommand.GetType() == typeof(float))  
  151.             {  
  152.                 coroutine.waitForTime = (float)yieldCommand;  
  153.                 coroutine.waitForTime  = (float)currentTime;  
  154.             }  
  155.             else if (yieldCommand.GetType() == typeof(CoroutineNode))  
  156.             {  
  157.                 coroutine.waitForCoroutine = (CoroutineNode)yieldCommand;  
  158.             }  
  159.             else if (yieldCommand is IYieldWrapper) //lonewolfwilliams  
  160.             {  
  161.                 coroutine.waitForUnityObject = yieldCommand as IYieldWrapper;  
  162.             }  
  163.             else  
  164.             {  
  165.                 throw new System.ArgumentException("CoroutineScheduler: Unexpected coroutine yield type: "   yieldCommand.GetType());  
  166.                   
  167.                 //this is an alternative if you don't have access to the function passed to the couroutineScheduler - maybe it's                        
  168.                 //precompiled in a dll for example - remember you will have to add a case every time you add a wrapper :/  
  169.                 /* 
  170.          var commandType = yieldCommand.GetType(); 
  171.      if(commandType == typeof(UnityEngine.WWW)) 
  172.          { 
  173.         coroutine.waitForUnityObject =  
  174.                new UnityWWWWrapper(yieldCommand as UnityEngine.WWW); 
  175.      } 
  176.      else if(commandType == typeof(UnityEngine.AsyncOperation)) 
  177.      { 
  178.         coroutine.waitForUnityObject =  
  179.            new UnityASyncOpWrapper(yieldCommand as UnityEngine.AsyncOperation); 
  180.      } 
  181.      else if(commandType == typeof(UnityEngine.AssetBundleRequest)) 
  182.      { 
  183.         coroutine.waitForUnityObject =  
  184.            new UnityAssetBundleRequestWrapper(yieldCommand as UnityEngine.AssetBundleRequest); 
  185.      } 
  186.      else 
  187.      { 
  188.             throw new System.ArgumentException("CoroutineScheduler: Unexpected coroutine yield type: "   yieldCommand.GetType()); 
  189.      } 
  190.          */  
  191.             }  
  192.         }  
  193.         else  
  194.         {  
  195.             // coroutine finished  
  196.             coroutine.finished = true;  
  197.             RemoveCoroutine(coroutine);  
  198.         }  
  199.     }  
  200.       
  201.     private void AddCoroutine (CoroutineNode coroutine)  
  202.     {  
  203.           
  204.         if (this.first != null) {  
  205.             coroutine.listNext = this.first;  
  206.             first.listPrevious = coroutine;  
  207.         }  
  208.         first = coroutine;  
  209.     }  
  210.       
  211.     private void RemoveCoroutine (CoroutineNode coroutine)  
  212.     {  
  213.         if (this.first == coroutine) {  
  214.             // remove first  
  215.             this.first = coroutine.listNext;  
  216.         } else {  
  217.             // not head of list  
  218.             if (coroutine.listNext != null) {  
  219.                 // remove between  
  220.                 coroutine.listPrevious.listNext = coroutine.listNext;  
  221.                 coroutine.listNext.listPrevious = coroutine.listPrevious;  
  222.             } else if (coroutine.listPrevious != null) {  
  223.                 // and listNext is null  
  224.                 coroutine.listPrevious.listNext = null;  
  225.                 // remove last  
  226.             }  
  227.         }  
  228.         coroutine.listPrevious = null;  
  229.         coroutine.listNext = null;  
  230.     }  
  231.       
  232. }//class  



CoroutineNode.cs 

  1. using System.Collections;  
  2. using UnityEngine;  
  3.   
  4.   
  5. /// <summary>  
  6. /// CoroutineNode.cs  
  7. ///   
  8. /// Port of the Javascript version from   
  9. /// http://www.unifycommunity.com/wiki/index.php?title=CoroutineScheduler  
  10. ///   
  11. /// Linked list node type used by coroutine scheduler to track scheduling of coroutines.  
  12. ///    
  13. /// BMBF Researchproject http://playfm.htw-berlin.de  
  14. /// PlayFM - Serious Games für den IT-gestützten Wissenstransfer im Facility Management   
  15. /// Gefördert durch das bmb f - Programm Forschung an Fachhochschulen profUntFH  
  16. ///   
  17. /// <author>Frank.Otto@htw-berlin.de</author>  
  18. ///  
  19. /// </summary>  
  20.   
  21. public class CoroutineNode  
  22. {  
  23.     public CoroutineNode listPrevious = null;  
  24.     public CoroutineNode listNext = null;  
  25.     public IEnumerator fiber;  
  26.     public bool finished = false;  
  27.     public int waitForFrame = -1;  
  28.     public float waitForTime = -1.0f;  
  29.     public CoroutineNode waitForCoroutine;  
  30.     public IYieldWrapper waitForUnityObject; //lonewolfwilliams  
  31.       
  32.     public CoroutineNode(IEnumerator _fiber)  
  33.     {  
  34.         this.fiber = _fiber;  
  35.     }  
  36. }  

IYieldWrapper.cs

  1. /* 
  2.  * gareth williams  
  3.  * http://www.lonewolfwilliams.com 
  4.  */  
  5.   
  6. public interface IYieldWrapper  
  7. {  
  8.     bool finished { get; }  
  9. }  

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

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.   
  6. /* 
  7. *   Gareth Williams 
  8. *   http://www.lonewolfwilliams.com 
  9. */  
  10.   
  11. class UnityASyncOpWrapper : IYieldWrapper  
  12. {  
  13.     private UnityEngine.AsyncOperation m_UnityObject;  
  14.     public bool finished  
  15.     {  
  16.         get  
  17.         {  
  18.             return m_UnityObject.isDone;  
  19.         }  
  20.     }  
  21.       
  22.     public UnityASyncOpWrapper(UnityEngine.AsyncOperation wraps)  
  23.     {  
  24.         m_UnityObject = wraps;  
  25.     }  
  26. }  

UnityWWWYieldWrapper 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.    
  6. /* 
  7.  * Gareth Williams 
  8.  * http://www.lonewolfwilliams.com 
  9.  */  
  10.    
  11. public class UnityWWWYieldWrapper : IYieldWrapper  
  12. {  
  13.    private UnityEngine.WWW m_UnityObject;  
  14.    public bool finished  
  15.    {  
  16.       get  
  17.       {  
  18.          return m_UnityObject.isDone;  
  19.       }  
  20.    }  
  21.    
  22.    public UnityWWWYieldWrapper(UnityEngine.WWW wraps)  
  23.    {  
  24.       m_UnityObject = wraps;  
  25.    }  
  26. }  
http://blog.csdn.net/u010019717/article/details/46801955

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论