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<IEnumerator> executionStack;

		public EditorCoroutine(IEnumerator iterator)
		{
			this.executionStack = new Stack<IEnumerator>();
			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<EditorCoroutine> editorCoroutineList;
	private static List<IEnumerator> buffer;

	public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
	{
		if (editorCoroutineList == null)
		{
            // test
			editorCoroutineList = new List<EditorCoroutine>();
		}
		if (buffer == null)
		{
			buffer = new List<IEnumerator>();
		}
		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的情況比較特殊,雖然理論上也是會有問題的,但是確實可以正常的取到結果。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章