当前位置:网站首页>Unity实用框架(二)协程管理框架
Unity实用框架(二)协程管理框架
2022-07-19 05:15:00 【p_帽子戏法】
Unity实用框架(二)协程管理框架
线程是一项由操作系统控制的资源。使用线程,在多核处理器中,可以显著地提高处理器的利用率。但事实上,在大多数情况下,我们需要的仅仅是代码并发执行的能力,在这种情况下,无需任何系统操作,全程在用户态上执行的协程,自然能够大展身手。
同时,由于unity monobehaviour的调用使用了c#的反射机制,而反射操作本身是具有可观开销的,因此,使用Coroutine全面取代Mono Update也可以有效的减轻mono的反射开销,从而达到优化代码的目的。
C# unity自带的Coroutine提供了最基础的使用协程的接口,在此基础上,笔者将提供一种能够适用于更多场景、更容易由程序员全权控制的协程管理框架.
CoroutineManager
调用过程
首先,CoroutineManager将维护以下这些协程数据结构:
//协程池中的协程
private Coroutine[] coroutines = new Coroutine[InitialSize];
//当前协程数
private int tail = 0;
//等待被执行的协程
private List<Coroutine> waitQueue = new List<Coroutine>(InitialSize);
//等待被停止的协程
private List<Coroutine> stopWaitQueue = new List<Coroutine>();
//由于父对象而需要被停止的协程,这里容器中存放的是Object而非Coroutine,代表该父对象并不局限于协程间的父子关系。
private List<UnityEngine.Object> stopByParentWaitQueue = new List<UnityEngine.Object>();
在CoroutineManager的update中,首先,结束stopWaitQueue中的协程及其子协程:
lock (stopWaitQueue)
{
for (int i = 0; i < tail; i++)
{
for (int j = 0; j < stopWaitQueue.Count; j++)
{
//IsChildOf为Coroutine类的函数,用来判断某协程是否为另一个协程的子协程
//这里使用的Coroutine是自定义的而非unity自带的,为了方便,也可以通过this关键字去扩展unity的Coroutine
if (coroutines[i].IsChildOf(stopWaitQueue[j]))
{
coroutines[i].Stop();
coroutines[i] = null;
break;
}
}
}
stopWaitQueue.Clear();
}
结束stopByParentWaitQueue规定应当结束的协程。
lock (stopByParentWaitQueue)
{
for (int i = 0; i < tail; i++)
{
for (int j = 0; j < stopByParentWaitQueue.Count; j++)
{
if (coroutines[i] != null &&
coroutines[i].ParentObject == stopByParentWaitQueue[j])
{
coroutines[i].Stop();
coroutines[i] = null;
break;
}
}
}
stopByParentWaitQueue.Clear();
}
正常进行协程。
for (int i = 0; i < tail; i++)
{
if (coroutines[i] == null)
{
continue;
}
coroutines[i] = coroutines[i].Restart();
}
最后,从协程数组中移除已经被置为空,即已失效的协程。
int i, j = 1;
for (i = 0; i < tail; i++, j++)
{
if (coroutines[i] == null)
{
for (; j < tail; j++)
{
if (coroutines[j] != null)
{
coroutines[i] = coroutines[j];
coroutines[j] = null;
break;
}
}
if (j == tail)
{
break;
}
}
}
tail = i;
在lateUpdate中,waitQueue中的协程将被加载到coroutine数组中,以便于下一帧执行。根据在生成协程中赋予的priority,调整加入coroutine后的顺序使得高优先级协程被首先执行。
lock (waitQueue)
{
if (waitQueue.Count > 0)
{
for (int i = 0; i < waitQueue.Count; i++)
{
if (tail == coroutines.Length)
{
Array.Resize(ref coroutines, checked(tail * 2));
}
coroutines[tail++] = waitQueue[i];
for (int j = tail - 1; j >= 1; j--)
{
if (coroutines[j - 1] == null || coroutines[j - 1].Priority < coroutines[j].Priority)
{
var temp = coroutines[j];
coroutines[j] = coroutines[j - 1];
coroutines[j - 1] = temp;
}
}
}
waitQueue.Clear();
}
}
使用接口
为了与系统的Coroutine保持一致,这里给外部调用的接口也命名为StartCoroutine。但这里的StartCoroutine将要求协程的发起者提供更多信息。
public Coroutine StartCoroutine(Coroutine coroutine, bool startImmediately = true)
最简单的接口,这种调用方式会使得将要被执行的协程被加入到WaitQueue中,在下一帧得到执行。如果需要立即开始执行,第二个参数应当被置为真。默认情况下,协程应当立即执行。
public Coroutine StartCoroutine(UnityEngine.Object parentObject, Coroutine coroutine, bool startImmediately = true)
此接口需要调用者指定协程的父对象。对于它的使用场景,比如,一个忍者对象释放了影分身,它的所有分身被一个Coroutine控制。当其本体死亡时,要求所有的Coroutine一同终结,这时,只需要在创建协程时将协程的父对象绑定到该本体上即可。
public Coroutine StartCoroutine(UnityEngine.Object parentObject, IEnumerator context, bool startImmediately = true, int priority = 0)
基本同2,但调用者不需要创建一个Coroutine对象,而只需要使用自己的IEumerator上下文即可,这使得调用者的写法更加灵活。但在这种情况下,调用者必须指定协程父对象。此priority同上面的isChildOf,需要自定义或扩展。
public void StopCoroutine(Coroutine coroutine)
终止一个协程。不管此协程处于running状态还是queue状态,直接将其从coroutine数组或waitQueue中移除。
public void StopCoroutinesOf(UnityEngine.Object parentObject)
终止某个对象下的子协程。同4,但此时要根据stopByParentWaitQueue来判断协程是否为该对象的子对象。在第2条给出的例子中,就可以调用此接口完成任务。
更多地,为了更方便调试,在coroutineManager中封装日志管理的功能也是有必要的,可以通过注册Unity自带的UnityEngine.Application.logMessageReceive事件,加上自己的逻辑,来完成这一功能。
Coroutine(自定义)
协程继承自IEnumerator,显然是理所应当的。
首先是协程应当拥有的一些属性:
internal int Priority { get; set; }
protected Exception error;
在最简单的协程中,只需要实现MoveNext和Restart两个函数,就可以实现协程拥有的功能。
更新于2022.7.16,未完待续
边栏推荐
- Intelligent gateway based on Ruixin micro 3568 core board
- Secure Code Warrlor学习记录(三)
- (data and electricity) summary of various triggers - FPGA octet (1)
- FPGA network port implementation and detailed explanation (3)
- 2022-7-12 第八小组 顾宇佳 (Js)
- 使用小技巧(一)
- When the vivado project version is upgraded, the relevant IP version IP status displays using cached IP results
- Detailed explanation of the principle of triode series linear voltage stabilizing circuit and Multisim Simulation
- Redux main knowledge learning summary
- The Debian system is ported with USBWiFi rtl8192eu driver and set to start automatically
猜你喜欢
代码审计之若依系统
方向信号的表达——复指数信号
Solution of STM32 cubeide breakpoint failure
【Markdown】关于Markdown我想说这些~
[DOM] first knowledge of DOM
[Lora & nb IOT] current situation analysis
Automatic nucleic acid extraction instrument based on ARM core board
How to set the oil on the through hole cover when exporting the Gerber file of PCB
內網滲透隧道技術的相關知識
SSH协议中隧道与代理的用法详解
随机推荐
Wireless network test based on Feiling NXP i.mx6ull
FPGA -- detailed explanation of the principle of displaying images with VGA timing (2)
乐山师范程序设计大赛2020-A: 好数对
2022-7-12 第八小组 顾宇佳 (Js)
解决问题phpstudy导入数据库失败
G2l series core board -rz/g2l processor introduction | frame diagram | power consumption | schematic diagram and hardware design guide
使用Google colab进行机器学习的经验
vulnhub 靶机 GOLDENEYE: 1
FPGA - detailed explanation of SPI bus (concept)
Chrome developer tool shortcut key reference to improve development efficiency
我的2020年线上的夏令营总结
[vscade configuration markdown environment] user friendly~
常见端口及漏洞
SSH协议中隧道与代理的用法详解
Thinkphp5 verification code
Secure Code Warrlor学习记录(四)
Allegro (cadence) export Gerber file steps
线性卷积、循环卷积、周期卷积的定义、计算方法及三者之间的关系
常见设备默认口令
内网渗透隧道技术的相关知识