Unity如何在Editor下执行协程(coroutine)

        在处理Unity5新的AssetBundle的时候,我有一个需求,需要在Editor下(比如一个menuitem的处理函数中,游戏没有运行,也没有MonoBehaviour)加载AssetBundle。而加载AssetBundle的时候又需要使用yield return www;这样的协程用法。

       所以就有了一个需求,在Editor下执行协程。我从网上找到一个EditorCoroutine,其代码如下:

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

public static class EditorCoroutineRunner
{
	private class EditorCoroutine : IEnumerator
	{
		private Stack executionStack;

		public EditorCoroutine(IEnumerator iterator)
		{
			this.executionStack = new Stack();
			this.executionStack.Push(iterator);
		}

		public bool MoveNext()
		{
			IEnumerator i = this.executionStack.Peek();

			if (i.MoveNext())
			{
				object result = i.Current;
				if (result != null && result is IEnumerator)
				{
					this.executionStack.Push((IEnumerator)result);
				}

				return true;
			}
			else
			{
				if (this.executionStack.Count > 1)
				{
					this.executionStack.Pop();
					return true;
				}
			}

			return false;
		}

		public void Reset()
		{
			throw new System.NotSupportedException("This Operation Is Not Supported.");
		}

		public object Current
		{
			get { return this.executionStack.Peek().Current; }
		}

		public bool Find(IEnumerator iterator)
		{
			return this.executionStack.Contains(iterator);
		}
	}
		
	private static List editorCoroutineList;
	private static List buffer;

	public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
	{
		if (editorCoroutineList == null)
		{
            // test
			editorCoroutineList = new List();
		}
		if (buffer == null)
		{
			buffer = new List();
		}
		if (editorCoroutineList.Count == 0)
		{
			EditorApplication.update += Update;
		}

		// add iterator to buffer first
		buffer.Add(iterator);
	
		return iterator;
	}

	private static bool Find(IEnumerator iterator)
	{
		// If this iterator is already added
		// Then ignore it this time
		foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
		{
			if (editorCoroutine.Find(iterator))
			{
				return true;
			}
		}

		return false;
	}

	private static void Update()
	{
		// EditorCoroutine execution may append new iterators to buffer
		// Therefore we should run EditorCoroutine first
		editorCoroutineList.RemoveAll
		(
			coroutine => { return coroutine.MoveNext() == false; }
		);

		// If we have iterators in buffer
		if (buffer.Count > 0)
		{
			foreach (IEnumerator iterator in buffer)
			{
				// If this iterators not exists
				if (!Find(iterator))
				{
					// Added this as new EditorCoroutine
					editorCoroutineList.Add(new EditorCoroutine(iterator));
				}
			}

			// Clear buffer
			buffer.Clear();
		}

		// If we have no running EditorCoroutine
		// Stop calling update anymore
		if (editorCoroutineList.Count == 0)
		{
			EditorApplication.update -= Update;
		}
	}
}

        这里需要注意几个地方:

1、EditorApplication.update,这个是一个delegate,可以绑定一个函数,从而在编辑器下执行Update。

2、EditorCoroutineRunner.StartEditorCoroutine(Routine1());  这样可以在编辑器下开启一个协程。

3、另外一个思路是不使用协程,绑定一个Update函数,然后判断www.isDone来获取AssetBundle。这个我并没有实际验证。

4、www可以正常的加载出AssetBundle,但是isDone的变量一直为false。额外要注意因为Editor模式下不存在退出游戏清理资源的概念,所以要注意处理已加载的assetbundle的情况,否则可能会报冲突的错误。

5、理论上只支持yield return null这样的情况,延时要自己处理。Unity协程的原理是引擎在特定条件下执行MoveNext运行下面的语句,在上面的代码中不管是延时还是其他的东西,都是每帧执行MoveNext,这样WaitForSeconds这样的协程是无效的。  www的情况比较特殊,虽然理论上也是会有问题的,但是确实可以正常的取到结果。


你可能感兴趣的:(Unity3D开发)