在處理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的情況比較特殊,盡管理論上也是會有問題的。可是確實能夠正常的取到結果。
