本文為博主原創(chuàng)文章,歡迎轉(zhuǎn)載,請保留出處:http://blog.csdn.net/andrewfan
Unity編輯器中何時需要協(xié)程
當(dāng)我們定制Unity編輯器的時候,往往需要啟動額外的協(xié)程或者線程進(jìn)行處理。比如當(dāng)執(zhí)行一些界面更新的時候,需要大量計算,如果用戶在不斷修正一個參數(shù),比如從1變化到2,這種變化過程要經(jīng)歷無數(shù)中間步驟,調(diào)用N多次Update,如果直接在Update中不斷刷新,界面很容易直接卡死。所以在一個協(xié)程中進(jìn)行一些優(yōu)化,只保留用戶最后一次參數(shù)修正,省去中間步驟,就會好很多。這屬于Unity編輯器的內(nèi)容,也屬于優(yōu)化的內(nèi)容,還是放在優(yōu)化中吧。
解決問題思路
Unity官網(wǎng)的questions里面也有很多人在搜索這個問題,不過后來是看到有個人提到了這個方法。問題的關(guān)鍵點(diǎn)就是“EditorApplication.update ”,有個這樣的方法,你把要執(zhí)行的協(xié)程傳遞給它就可以在編輯器下自動執(zhí)行循環(huán)調(diào)用。
老外的寫法
當(dāng)然,后來我也找到一個老外的寫法,代碼貼出來如下:
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) { 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; } } }
用法就是大概在你自己的類的Start方法中稍作修改,再增加一個協(xié)程函數(shù),如下:
void Start() { rope = gameObject.GetComponent<QuickRope>(); #if UNITY_EDITOR //調(diào)用方法 EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop()); #endif } public IEnumerator OnThreadLoop() { while(true) { Debug.Log("Looper"); yield return null; } }
當(dāng)然最好是加上#if UNITY_EDITOR預(yù)處理了。這個類基本是滿足要求了。如果你把你自己的腳本做了這樣的修改之后,它是可以在編輯狀態(tài)不斷執(zhí)行到Loop的,要注意它需要先執(zhí)行到Start,也就是說,你可能需要把GameObject做成Prefab,然后把它從場景中刪除,再把Prefab拖回場景,才會在編輯狀態(tài)下觸發(fā)腳本上的Star方法,從而激發(fā)Loop。
我的寫法
然而,用久了你就會發(fā)現(xiàn)幾個問題,一旦Loop開始了,你是無法停止的,哪怕你把GameObject從場景中刪掉都無濟(jì)于事,當(dāng)然隱藏也沒有效果。為了解決這個問題,也把腳本弄得簡單點(diǎn)兒,我重寫了這個腳本,希望需要的同學(xué)可以愉快地使用。
using UnityEngine;using UnityEditor;using System.Collections;using System.Collections.Generic;using System.Runtime.CompilerServices;public static class EditorCoroutineLooper{ private static Dictionary<IEnumerator,MonoBehaviour> m_loopers = new Dictionary<IEnumerator,MonoBehaviour> (); private static bool M_Started = false; /// <summary> /// 開啟Loop /// </summary> /// <param name="mb">腳本</param> /// <param name="iterator">方法</param> public static void StartLoop(MonoBehaviour mb, IEnumerator iterator) { if(mb!=null && iterator != null) { if(!m_loopers.ContainsKey(iterator)) { m_loopers.Add(iterator,mb); } else { m_loopers[iterator]=mb; } } if (!M_Started) { M_Started = true; EditorApplication.update += Update; } } private static List<IEnumerator> M_DropItems=new List<IEnumerator>(); private static void Update() { if (m_loopers.Count > 0) { var allItems = m_loopers.GetEnumerator(); while(allItems.MoveNext()) { var item = allItems.Current; var mb = item.Value; //卸載時丟棄Looper if(mb == null) { M_DropItems.Add(item.Key); continue; } //隱藏時別執(zhí)行Loop if(!mb.gameObject.activeInHierarchy) { continue; } //執(zhí)行Loop,執(zhí)行完畢也丟棄Looper IEnumerator ie = item.Key; if(!ie.MoveNext()) { M_DropItems.Add(item.Key); } } //集中處理丟棄的Looper for(int i = 0;i < M_DropItems.Count;i++) { if(M_DropItems[i] != null) { m_loopers.Remove(M_DropItems[i]); } } M_DropItems.Clear(); } if (m_loopers.Count == 0) { EditorApplication.update -= Update; M_Started = false; } } }
//調(diào)用方法原來這個樣 EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop());//現(xiàn)在改成這個樣 EditorCoroutineLooper.StartLoop(this,OnThreadLoop());
使用這個腳本的時候,需要傳兩個參數(shù),一個就是你自己的腳本,另外一個就是協(xié)程函數(shù)。原理就是代碼里面會檢測你的腳本狀態(tài),當(dāng)腳本關(guān)閉或者卸載的時候,都會停掉Loop調(diào)用。老外有時候?qū)懘a,也不那么講究,有沒有?
http://www.cnblogs.com/driftingclouds/p/6603287.html