【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. 今天就 ...
随机推荐
- JavaScript 语法
一.JavaScript简介 1.JavaScript是个什么东西? 它是个脚本语言,需要有宿主文件,它的宿主文件是HTML文件. 2.它与Java什么关系? 没有什么直接的联系,Java是Sun公司 ...
- block和delegate的区别
代理 可读性高 大部分可以属性 block 写的代码少 一般作为参数通知 占用资源 无论是block还是delegate模式本质上都是回调,使用block,其优点是回调的block代码块直 ...
- HUST 1541 Student’s question
1541 - Student’s question 时间限制:1秒 内存限制:128兆 696 次提交 134 次通过 题目描述 YYis a student. He is tired of calc ...
- Ninject之旅之十二:Ninject在Windows Form程序上的应用(附程序下载)
摘要: 下面的几篇文章介绍如何使用Ninject创建不同类型的应用系统.包括: Windows Form应用系统 ASP.NET MVC应用系统 ASP.NET Web Form应用系统 尽管对于不同 ...
- Ajax 入门之 GET 与 POST 的不同 (2)
在之前的随笔中,本着怀旧的态度总结了一篇 兼容不同浏览器 建立XHR对象的方法: 在建立好XHR对象之后,客户端需要做的就是,将数据以某种方式传递到服务器,以获得相应的响应,在这里, Ajax技术总 ...
- matlab 逻辑数组及其应用
这几天学习了matlab的逻辑数组功能,总的感觉就有两点: 第一,通过对原来的数组a进行逻辑判断后得到逻辑数组b 第二,逻辑数组进行某种运算符操作又回到原数组类型 第三,利用逻辑数组mask功能 测试 ...
- JDBC连接数据库(二)——连接池
参考博客:http://www.cnblogs.com/xdp-gacl/p/4002804.html 一. 应用程序直接建立数据库连接模型 应用程序直接每次访问数据库时,都建立创建一个数据库的链接, ...
- 如何居中div?如何居中一个浮动元素?
如何居中div? 给div设置一个宽度,然后添加margin:0 auto属性 div{ width:200px; margin:0 auto; } 如何居中一个浮动元素? <!DOCTYPE ...
- UWP: ListView 中与滚动有关的两个需求的实现
在 App 的开发过程中,ListView 控件是比较常用的控件之一.掌握它的用法,能帮助我们在一定程度上提高开发效率.本文将会介绍 ListView 的一种用法--获取并设置 ListView 的滚 ...
- 记使用sed的一次坑
sed做为linux下的三剑客,自然功能强大,但是如果使用不当,反而适得其反,今天就因为这个命令采了很深坑,分析一下原因,以谏后来者. 情景回顾: 项目中使用的一个python爬虫采用的是多线程并发爬 ...