unity在foreach里能做unity协程和线程吗

本文为博主原创文章,欢迎转载,请保留出处:
Unity编辑器中何时需要协程
当我们定制Unity编辑器的时候,往往需要启动额外的协程或者线程进行处理。比如当执行一些界面更新的时候,需要大量计算,如果用户在不断修正一个参数,比如从1变化到2,这种变化过程要经历无数中间步骤,调用N多次Update,如果直接在Update中不断刷新,界面很容易直接卡死。所以在一个协程中进行一些优化,只保留用户最后一次参数修正,省去中间步骤,就会好很多。这属于Unity编辑器的内容,也属于优化的内容,还是放在优化中吧。
解决问题思路
Unity官网的questions里面也有很多人在搜索这个问题,不过后来是看到有个人提到了这个方法。问题的关键点就是“EditorApplication.update ”,有个这样的方法,你把要执行的协程传递给它就可以在编辑器下自动执行循环调用。
老外的写法
当然,后来我也找到一个老外的写法,代码贴出来如下:
using UnityE
using UnityE
using System.C
using System.Collections.G
using System.Runtime.CompilerS
public static class EditorCoroutineRunner
private class EditorCoroutine : IEnumerator
private Stack&IEnumerator& executionS
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.C
if (result != null && result is IEnumerator)
this.executionStack.Push((IEnumerator)result);
if (this.executionStack.Count & 1)
this.executionStack.Pop();
public void Reset()
throw new System.NotSupportedException("This Operation Is Not Supported.");
public object Current
get { return this.executionStack.Peek().C }
public bool Find(IEnumerator iterator)
return this.executionStack.Contains(iterator);
private static List&EditorCoroutine& editorCoroutineL
private static List&IEnumerator&
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 += U
// add iterator to buffer first
buffer.Add(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))
private static void Update()
// EditorCoroutine execution may append new iterators to buffer
// Therefore we should run EditorCoroutine first
editorCoroutineList.RemoveAll
coroutine =& { return coroutine.MoveNext() == }
// 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 -= U
用法就是大概在你自己的类的Start方法中稍作修改,再增加一个协程函数,如下:
void Start()
rope = gameObject.GetComponent&QuickRope&();
#if UNITY_EDITOR
//调用方法
EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop());
public IEnumerator OnThreadLoop()
while(true)
Debug.Log("Looper");
当然最好是加上#if UNITY_EDITOR预处理了。这个类基本是满足要求了。如果你把你自己的脚本做了这样的修改之后,它是可以在编辑状态不断执行到Loop的,要注意它需要先执行到Start,也就是说,你可能需要把GameObject做成Prefab,然后把它从场景中删除,再把Prefab拖回场景,才会在编辑状态下触发脚本上的Star方法,从而激发Loop。
然而,用久了你就会发现几个问题,一旦Loop开始了,你是无法停止的,哪怕你把GameObject从场景中删掉都无济于事,当然隐藏也没有效果。为了解决这个问题,也把脚本弄得简单点儿,我重写了这个脚本,希望需要的同学可以愉快地使用。
using UnityE
using UnityE
using System.C
using System.Collections.G
using System.Runtime.CompilerS
public static class EditorCoroutineLooper
private static Dictionary&IEnumerator,MonoBehaviour& m_loopers = new Dictionary&IEnumerator,MonoBehaviour& ();
private static bool M_Started =
/// &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);
m_loopers[iterator]=
if (!M_Started)
M_Started =
EditorApplication.update += U
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.C
var mb = item.V
//卸载时丢弃Looper
if(mb == null)
M_DropItems.Add(item.Key);
//隐藏时别执行Loop
if(!mb.gameObject.activeInHierarchy)
//执行Loop,执行完毕也丢弃Looper
IEnumerator ie = item.K
if(!ie.MoveNext())
M_DropItems.Add(item.Key);
//集中处理丢弃的Looper
for(int i = 0;i & M_DropItems.Ci++)
if(M_DropItems[i] != null)
m_loopers.Remove(M_DropItems[i]);
M_DropItems.Clear();
if (m_loopers.Count == 0)
EditorApplication.update -= U
M_Started =
//调用方法原来这个样
EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop());
//现在改成这个样
EditorCoroutineLooper.StartLoop(this,OnThreadLoop());
使用这个脚本的时候,需要传两个参数,一个就是你自己的脚本,另外一个就是协程函数。原理就是代码里面会检测你的脚本状态,当脚本关闭或者卸载的时候,都会停掉Loop调用。老外有时候写代码,也不那么讲究,有没有?
阅读(...) 评论()没有更多推荐了,
不良信息举报
举报内容:
unity3D中使用协程来做“多次调用一次更新”特性的一个大坑
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!没有更多推荐了,
不良信息举报
举报内容:
unity3D中使用协程来做“多次调用一次更新”特性的一个大坑。
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!

我要回帖

更多关于 unity 协程性能 的文章

 

随机推荐