神马都是浮云,unity中自己写Coroutine协程源码

翻译 2015年07月21日 08:55:15

孙广东   2014.7.19

无意之间看到了,Unity维基上的一篇文章,  是关于自己写协程的介绍。   觉得很好,这样能更好的了解到协程的运行机制等特性,还是不错的。

原文链接地址如下:

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;
   }
}







协程--Coroutine小记

在unity中,协程是一个特殊的函数,它通过使用yield语句中断执行当前的代码,直到中断指令(YieldInstruction)结束后再接着之前的代码继续执行。 在C#中写协程需要遵循的规则有: ...
  • Willtom
  • Willtom
  • 2016年11月12日 10:08
  • 2152

Unity3D中的Coroutine详解

Unity中的Coroutine原理猜测:虚拟机分段执行机制, 同类型嵌套用栈存放实现串行执行. Unity中使用yield Coroutine需要注意的问题...
  • Blues1021
  • Blues1021
  • 2014年11月09日 23:29
  • 44797

协程(二) 云风coroutine源码分析

云风的coroutine代码量少、简洁,非常适合用来入门协程。从中我们可以学习到协程的设计思路,例如控制权的传递、调度器的设计、协程栈的使用等概念。...
  • u010599509
  • u010599509
  • 2016年08月29日 17:54
  • 1776

unity简单设计模式---CoroutineScheduler

Contents [hide] 1 Description2 Usage3 Code 3.1 CoroutineScheduler.js3.2 CoroutineNode.js3.3 Coroutin...
  • u010019717
  • u010019717
  • 2015年03月15日 21:54
  • 1494

Unity在协程(Coroutines)内开启线程(Threading )

为什么要在协程中开启线程, 因为很多时候我们是需要线程执行完成后回到主线程的。然后主线程在继续执行后续的操作。首先,Unity官方也提到过《我的应用为什么应该使用线程而不是协程?》先说协程,协程方法可...
  • u010019717
  • u010019717
  • 2017年06月13日 09:35
  • 1668

[Unity基础]对Coroutine的一些理解

相关链接: http://dsqiu.iteye.com/blog/2029701 http://www.cocos2dev.com/?p=496 http://7dot9.com/?p=6...
  • lyh916
  • lyh916
  • 2015年03月10日 20:53
  • 1360

神马都是浮云,unity中自己写Coroutine协程源码

无意之间看到了,Unity维基上的一篇文章,  是关于自己写协程的介绍。   觉得很好,这样能更好的了解到协程的运行机制等特性,还是不错的。原文链接地址如下:http://wiki.unity3d.c...
  • u010019717
  • u010019717
  • 2015年07月21日 08:55
  • 2819

【Unity优化】如何实现Unity编辑器中的协程

Unity编辑器中何时需要协程当我们定制Unity编辑器的时候,往往需要启动额外的协程或者线程进行处理。比如当执行一些界面更新的时候,需要大量计算,如果用户在不断修正一个参数,比如从1变化到2,这种变...
  • AndrewFan
  • AndrewFan
  • 2017年03月22日 22:46
  • 3362

Unity3D - Unity协程(Coroutine)原理深入剖析

本文只是从Unity的角度去分析理解协程的内部运行原理,而不是从C#底层的语法实现来介绍(后续有需要再进行介绍),一共分为三部分:线程(Thread)和协程(Coroutine) Unity中协程的...
  • honey199396
  • honey199396
  • 2016年08月31日 14:26
  • 1349

Unity协程(Coroutine)原理深入剖析

尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com                  记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当...
  • OnafioO
  • OnafioO
  • 2015年10月08日 21:43
  • 8040
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:神马都是浮云,unity中自己写Coroutine协程源码
举报原因:
原因补充:

(最多只允许输入30个字)