【Unity优化】如何实现Unity编辑器中的协程
本文为博主原创文章,欢迎转载,请保留出处: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编辑器中的协程的更多相关文章
- 【Unity优化】怎样实现Unity编辑器中的协程
Unity编辑器中何时须要协程 当我们定制Unity编辑器的时候,往往须要启动额外的协程或者线程进行处理.比方当运行一些界面更新的时候,须要大量计算,假设用户在不断修正一个參数,比方从1变化到2.这种 ...
- Unity中的协程是什么?
什么是协程? 1.协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码. 2.Unity在每一帧(Frame)都会去处理对象上的协程.Unit ...
- Unity中的协程(一)
这篇文章很不错的问题,推荐阅读英文原版: Introduction to Coroutines Scripting with Coroutines 这篇文章转自:http://blog.csdn. ...
- xlua 实现协程替换Unity中的协程
C#中的协程: IEnumerator ShowSpiritInfo() { UIMessageMgr.ShowMsgWait(true); DestroyUIModelInfo(); bool is ...
- lua中的协程
lua中的协程和线程类似: 1. 协程拥有自己的独立的栈,局部变量,和指令: 2. 所有协程都可以共享全局变量: 3. 协程不能像线程那样并行执行,协程之间需要相互协调执行,同一个时刻只能运行一个协程 ...
- 深入tornado中的协程
tornado使用了单进程(当然也可以多进程) + 协程 + I/O多路复用的机制,解决了C10K中因为过多的线程(进程)的上下文切换 而导致的cpu资源的浪费. tornado中的I/O多路复用前面 ...
- python中的协程及实现
1.协程的概念: 协程是一种用户态的轻量级线程.协程拥有自己的寄存器上下文和栈. 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回来的时候,恢复先前保存的寄存器上下文和栈. 因此,协程能保留 ...
- fasthttp中的协程池实现
fasthttp中的协程池实现 协程池可以控制并行度,复用协程.fasthttp 比 net/http 效率高很多倍的重要原因,就是利用了协程池.实现并不复杂,我们可以参考他的设计,写出高性能的应用. ...
- Golang 入门系列(六)理解Go中的协程(Goroutine)
前面讲的都是一些Go 语言的基础知识,感兴趣的朋友可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category/1275863.html. 今天就 ...
随机推荐
- iOS网络层设计感想
App的开发无外乎从网络端获取数据显示在屏幕上,数据做些缓存或者持久化,所以网络层极为重要.原来只是把AFNetwork二次封装了一下,使得调用变得很简单,并没有深层次的考虑一些问题. 前言 参考: ...
- 如何增强ArcGIS插值图出图效果
如何增强ArcGIS插值图出图效果 by 李远祥 在一些科研领域,经常会遇到使用插值的方式进行处理,并生成最终的插值图.插值图在ArcGIS里面非常容易生成,只要具备了采用点数据,通过ArcToolB ...
- 初识markdown以及markdown的常用语法
一直都听说markdown可以写作变得更加方便,但是总没有去了解一下,今天下载了一个markdown编辑器,感受了一下markdown的魅力,发现对于程序员写博客之类的,markdown带来了很大 ...
- phpcms 替换首页
利用phpcms制作企业站,首先要将静态的企业主页替换成后台可编辑的动态主页. 首先做一个静态的企业站主页: <!DOCTYPE html> <html> <head&g ...
- ubuntu中的apache的基本技巧
1. 显示apache的版本号 XXX@XXX-ThinkPad-Edge-E431:~$ apache2 -v Server version: Apache/ (Ubuntu) Server bui ...
- Eclipse中debug调试java代码一直报Source not found的解决办法
今天使用eclipse的debug调试代码,一直没法正常调试,一按F6就提示Source not found 根据提示发现可能是另一个项目影响了,所以把另一个项目Close Project,这次直接t ...
- SaberRD之交流分析
交流分析(AC Analysis)也叫做小信号(Small-Signal)分析,也即分析电路的小信号频率响应,更严谨的定义是:分析工作在直流偏置电压下的非线性电路对于一定频率范围内的输入小信号的系统响 ...
- tornado学习 - TCPServer 实现聊天功能
最近学习tornado框架,其中有很多值得学习的模块,鉴于某位学长的建议,也决定好好看看tornado.tcpserver. 关于TCP协议详细介绍可查阅wiki. 对于TCP服务器,基本的操作tor ...
- 推荐几款主流的Css Reset
1.Eric Meyer’s “Reset CSS”(重置的很极端) 官方网址:CSS Tools: Reset CSS 2.HTML5 Doctor CSS Reset 官方网址:HTML5 Res ...
- BI数据分析中KPI,KGI,CSF概念
1. 行为产生数据 先来谈一谈,自己对数据基础概念的思考.我认为首先要建立的核心观点是:行为产生数据. 翻译一下这个核心观点.意思就是,当我们在思考或描述数据相关需求的时候,必然要包含这样的语素:&q ...