【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. 今天就 ...
随机推荐
- 内功心法 -- java.util.ArrayList<E> (6)
写在前面的话:读书破万卷,编码如有神--------------------------------------------------------------------下文主要对java.util ...
- GIS制图课程前言
一直以来都想写一本关于电子地图制图的工具书,把过去所遇到的关于电子地图制图的种种方法.技巧和问题进行总结和归纳,传播电子地图制图的思路和心得. 从2010年开始,陆陆续续在华南地区开展了多场的电子地图 ...
- 使用jsCompress压缩混淆js代码的一些常见的问题和技巧
不同的团队使用的js混淆器或压缩工具不一样,jsCompress是一款绿色的免费的js压缩工具,时代定制的UI团队推荐大家使用,不仅性能优越,而且操作非常人性化. 使用jsCompress.exe时, ...
- HUST 1584 摆放餐桌
1584 - 摆放餐桌 时间限制:1秒 内存限制:128兆 609 次提交 114 次通过 题目描述 BG准备在家办一个圣诞晚宴,他用一张大桌子招待来访的客人.这张桌子是一个圆形的,半径为R.BG邀请 ...
- 众人拾柴火焰高之Tomcat集群
一人拾柴火不旺,众人拾柴火焰高.Tomcat服务器也是一样,一台服务器再强大能承受的访问也是有限的.要提供高并发.高可用的服务,就必须横向扩展,多台Tomcat组成一个集群,根据实际的访问量动态增减服 ...
- Raid类型
raid0:两块磁盘同时写入raid1:一块磁盘写入,另一块磁盘做备份,利用率50%raid5:两块磁盘同时写入两块磁盘都有内容,第三块磁盘做效应,n/n-1的利用率raid6:四块磁盘两块磁盘做存储 ...
- ios 动画学习的套路 (二)
有它们俩你就够了! 说明:下面有些概念我说的不怎么详细,网上实在是太多了,说了我觉得也意义不大了!但链接都给大家了,可以自己去看,重点梳理学习写动画的一个过程和一些好的博客! (一) 说说这两个三方库 ...
- sqlite数据库学习
1.0版代码: package com.swust.sqlitedatabase.test; import com.swust.sqlitedatabase.myOpenHelper; import ...
- selenium框架与chrome浏览器的不兼容问题
在一次偶然的情况下,在chrome上用selenium框架去抓取某个id为XX的页面元素,使用WebDriver的findElement().click()方法进行点击,原来在firefox浏览器运行 ...
- thinkjs——一个字段一种数字代表两种状态
问题来源: 现有一张company数据表,其中有一个字段state(-2:待审核:-1:禁用:0:正常:1:会员过期:),一般而言,在前期设计数据表的时候,会将每种状态下都用一种特定的数字代表,但是这 ...