本文为博主原创文章,欢迎转载,请保留出处:http://blog.csdn.net/andrewfan

Unity编辑器中何时需要协程

当我们定制Unity编辑器的时候,往往需要启动额外的协程或者线程进行处理。比如当执行一些界面更新的时候,需要大量计算,如果用户在不断修正一个参数,比如从1变化到2,这种变化过程要经历无数中间步骤,调用N多次Update,如果直接在Update中不断刷新,界面很容易直接卡死。所以在一个协程中进行一些优化,只保留用户最后一次参数修正,省去中间步骤,就会好很多。这属于Unity编辑器的内容,也属于优化的内容,还是放在优化中吧。

解决问题思路

Unity官网的questions里面也有很多人在搜索这个问题,不过后来是看到有个人提到了这个方法。问题的关键点就是“EditorApplication.update ”,有个这样的方法,你把要执行的协程传递给它就可以在编辑器下自动执行循环调用。

老外的写法

当然,后来我也找到一个老外的写法,代码贴出来如下:

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方法中稍作修改,再增加一个协程函数,如下:

        void Start()
{
rope = gameObject.GetComponent<QuickRope>();
#if UNITY_EDITOR
//调用方法
EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop());
#endif
}
public IEnumerator OnThreadLoop()
{
while(true)
{
Debug.Log("Looper");
yield return null;
}
}

当然最好是加上#if UNITY_EDITOR预处理了。这个类基本是满足要求了。如果你把你自己的脚本做了这样的修改之后,它是可以在编辑状态不断执行到Loop的,要注意它需要先执行到Start,也就是说,你可能需要把GameObject做成Prefab,然后把它从场景中删除,再把Prefab拖回场景,才会在编辑状态下触发脚本上的Star方法,从而激发Loop。

我的写法

然而,用久了你就会发现几个问题,一旦Loop开始了,你是无法停止的,哪怕你把GameObject从场景中删掉都无济于事,当然隐藏也没有效果。为了解决这个问题,也把脚本弄得简单点儿,我重写了这个脚本,希望需要的同学可以愉快地使用。

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;
}
//隐藏时别执行Loop
if(!mb.gameObject.activeInHierarchy)
{
continue;
}
//执行Loop,执行完毕也丢弃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;
}
}
}
//调用方法原来这个样
EditorCoroutineRunner.StartEditorCoroutine(OnThreadLoop());
//现在改成这个样
EditorCoroutineLooper.StartLoop(this,OnThreadLoop());

使用这个脚本的时候,需要传两个参数,一个就是你自己的脚本,另外一个就是协程函数。原理就是代码里面会检测你的脚本状态,当脚本关闭或者卸载的时候,都会停掉Loop调用。老外有时候写代码,也不那么讲究,有没有?

【Unity优化】如何实现Unity编辑器中的协程的更多相关文章

  1. 【Unity优化】怎样实现Unity编辑器中的协程

    Unity编辑器中何时须要协程 当我们定制Unity编辑器的时候,往往须要启动额外的协程或者线程进行处理.比方当运行一些界面更新的时候,须要大量计算,假设用户在不断修正一个參数,比方从1变化到2.这种 ...

  2. Unity中的协程是什么?

    什么是协程? 1.协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码. 2.Unity在每一帧(Frame)都会去处理对象上的协程.Unit ...

  3. Unity中的协程(一)

    这篇文章很不错的问题,推荐阅读英文原版: Introduction to Coroutines Scripting with Coroutines   这篇文章转自:http://blog.csdn. ...

  4. xlua 实现协程替换Unity中的协程

    C#中的协程: IEnumerator ShowSpiritInfo() { UIMessageMgr.ShowMsgWait(true); DestroyUIModelInfo(); bool is ...

  5. lua中的协程

    lua中的协程和线程类似: 1. 协程拥有自己的独立的栈,局部变量,和指令: 2. 所有协程都可以共享全局变量: 3. 协程不能像线程那样并行执行,协程之间需要相互协调执行,同一个时刻只能运行一个协程 ...

  6. 深入tornado中的协程

    tornado使用了单进程(当然也可以多进程) + 协程 + I/O多路复用的机制,解决了C10K中因为过多的线程(进程)的上下文切换 而导致的cpu资源的浪费. tornado中的I/O多路复用前面 ...

  7. python中的协程及实现

    1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...

  8. fasthttp中的协程池实现

    fasthttp中的协程池实现 协程池可以控制并行度,复用协程.fasthttp 比 net/http 效率高很多倍的重要原因,就是利用了协程池.实现并不复杂,我们可以参考他的设计,写出高性能的应用. ...

  9. Golang 入门系列(六)理解Go中的协程(Goroutine)

    前面讲的都是一些Go 语言的基础知识,感兴趣的朋友可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category/1275863.html. 今天就 ...

随机推荐

  1. SpringBoot 入门教程:集成mybatis,redis

    SrpingBoot相较于传统的项目具有配置简单,能快速进行开发的特点,花更少的时间在各类配置文件上,更多时间在具体业务逻辑上. SPringBoot采用纯注解方式进行配置,不喜欢xml配置的同学得仔 ...

  2. list与数组转换

    1.数组转换list (1) List myList = new ArrayList(); String[] myStringArray = new String[] {"Java" ...

  3. 【LeetCode题解】链表Linked List

    1. 链表 数组是一种顺序表,index与value之间是一种顺序映射,以\(O(1)\)的复杂度访问数据元素.但是,若要在表的中间部分插入(或删除)某一个元素时,需要将后续的数据元素进行移动,复杂度 ...

  4. vm虚拟机Kali2.0实现与物理机之间的文件拖动共享

    MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...

  5. 在Express的页面模板中的变量的定义与使用总结

    前言 最近在使用Express框架中的ejs页面模板趟了些许坑,仅以本文记录总结. 本文简述的均为ejs页面模板. 创建ejs变量的各种方法 1. 在Nodejs定义的ejs变量 ejs由是在node ...

  6. BZOJ 3652: 大新闻(数位DP+概率论)

    不得不说数位DP和博弈论根本不熟啊QAQ,首先这道题嘛~~~可以分成两个子问题: 有加密:直接算出0~n中二进制每一位为0或为1分别有多少个,然后分位累加求和就行了= = 无加密:分别算出0~n中二进 ...

  7. 剑指offer编程题Java实现——面试题5从头到尾打印链表

    题目描述* 剑指offer面试题5:从尾到头打印链表 输入一个链表的头结点,从尾到头打印出每个结点的值 解决方案一:首先遍历链表的节点后打印,典型的"后进先出",可以使用栈来实现这 ...

  8. 【排序算法】直接选择排序算法 Java实现

    基本思想 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后. 初始状态:无序区为a[1...n],有序区为空. 第一次排序:在无序区a[1...n]中选出最小的记录a[k],将它与有序区的第 ...

  9. 【Zookeeper】源码分析之网络通信(一)

    一.前言 前面已经分析了请求处理链中的多数类,接着继续分析Zookeeper中的网络通信模块. 二.总体框图 对于网络通信模块,其总体框图如下所示 说明: Stats,表示ServerCnxn上的统计 ...

  10. react基础学习

    什么是react:React(有时称为React.js 或ReactJS)是一个为数据提供渲染HTML视图的开源JavaScript库; 其特点: 声明式设计:采用声明范式,可以轻松描述应用高效:通过 ...