Unity Notes之协程(Coroutine)简析

Unity D3D11渲染纹理创建失败:五大核心原因与修复方案 在实时图形渲染中,渲染纹理(RenderTexture)是实现屏幕后处理、动态阴影、多Pass渲染等高级效果的核心组件。其原理是通过在GPU显存中开辟一块可读写的缓冲区,作为离屏渲染目标,从而实现复杂的图像合成与处理流程。这项技术的价值在于突破了单次渲染的局限,为动态模糊、景深、HDR等现代图形特效提供了基础。在Unity引擎中,渲染纹理的创建与管理高度依赖于底层图形API(如Direct3D 11)。然而,当项目切换到D3D11图形接口时,开发者常会遇到因**抗锯齿(MSAA)设置冲突**、**深度缓冲区 阅读详情

协程(Coroutine)是Unity项目开发中经常使用的一个特性,其可以让代码以分时的方式执行,这样可以避免某帧中的复杂操作阻塞当前帧而导致给用户带来不流畅的体验。协程其不是C#的语言特性,但在实现中用到了C#中的迭代器等特性,因而如果能对实现机制其有更深入的理解的话也可以使得对协程的使用更加自如。

1. C#中 IEnumerator,IEnumerable

迭代器(IEnumerator)提供了一种可迭代对象(IEnumerable)的无差别访问接口,其主要有三个接口:
public object Current {get;}
public bool MoveNext()
public void Reset()
实现了上述三个接口后,再配合IEnumerable对象(需要实现IEnumerator GetEnumerator()的接口),就可以实现

foreach(Object obj in IEnumerableObject)
{
    // Some operations
}

像这样的遍历访问,很是方便。

2. 通过迭代器块便捷地实现迭代器

通过代码实现迭代器的话需要实现三个方法,较为麻烦,因而在C#中提供了一种通过迭代器块快速实现迭代器的方法。yield所在的代码段其实不同于普通的代码,C#编译器遇到yield之后会做特殊处理,然后将其编译为具有迭代器逻辑类型的代码。比如:

public class IEnumeratorObject : IEnumerator
{


}

public void Func1()
{
    // Some codes
}

public void Func2()
{
    // Some codes
}

public void FuncN()
{
    // Some codes
}

IEnumerator TestCode()
{
    Func1();
    yield return new IEnumeratorObject();

    Func2();
    yield return new IEnumeratorObject();

    //...
    FuncN();
    yield return new IEnumeratorObject();
}

public class CompliedIEnumeratorObject : IEnumerator
{
    // execute counter
    int mExecuteCounter = 1;
    
    public object Current
    {
        get
        {
            return null;
        }
    }

    public bool MoveNext()
    {
        if (mExecuteCounter == 1)
        {
            Func1();
            mExecuteCounter++;

            return true;
        }
        else if (mExecuteCounter == 2)
        {
            Func2();
            mExecuteCounter++;

            return true;
        }
        //...
        else if (mExecuteCounter == N)
        {
            FuncN();
            mExecuteCounter++;

            return true;
        }

        return false;
    }

    public void Reset()
    {

    }
}

3. Unity中基于迭代器的Coroutine

在C#中来看的话迭代器提供了可迭代对象的访问接口,然后除此之外看起来并没有更多的用处。但是在Unity中将该特性做了更多的利用,很好的使用了迭代器来实现了复杂代码的分片执行。比如一段较为复杂的操作,里边含有了较多的逻辑与运算:
public void ComplexFunc()
{
    int counter = 0;
    while (counter < 10)
    {
        // Some computation may cost 5 ms
        Func();
    }
}
比如其中的Func可能会耗时5ms,整个ComplexFunc就会消耗50ms,如果在游戏的一帧中完成上述操作的话必定会造成当前帧的卡顿,带来不好的游戏体验。如果该操作不是必需要阻塞执行的话,那么一个更好的方法是将ComplexFunc中的操作分散开来执行,比如在每一帧中我们执行一次Func,然后整个操作会在5帧之后完成,这样的话游戏就会对玩家有更流畅的表现。当然,为了达到此目的,我们可以添加计数器,然后在每个Update中调用对应的Func直到对应的计算结束。但是这里有些问题:
  • 需要自己管理复杂计算之间的相互关系,特别是每两次计算之间需要进行数据传递时;
  • 需要实现多种分时计算的方案,比如连续帧执行,跨帧执行,间隔一定时间执行,甚至上面的这些分时方式的任意组合;
这些都给自定义分时计算的实现带来了较多的问题,有较大的工作量,且调试会比较麻烦。不过幸好,Unity中提供的Coroutine可以帮助我们实现这样的功能。Coroutine的意义为协同程序,顾名思义,其具有协作执行的特性,其只能在MonoBehavior中使用,因其本身不具有Update的特性,需要借助于MonoBehavior的更新来完成状态的切换。Coroutine的实现大体如下(个人推测):
  • Coroutine为一个引擎底层的实现类,其中含有一个Run函数(或Update)主要来做调用时的执行操作,该方法用在MonoBehavior的更新中会调用,当然,这里的调用是在MonoBehavior的Updata、FixedUpdate和LateUpdate之外,其间的具体顺序可以参考Unity的官方文档。
  • StartCoroutine调用时传入的IEnumerator也会经过不同的编译,其中的每一个yield return 都会对应一个YieldInstruction的对象,比如WaitForSeconds等,Unity会将其处理成对应的延时对象,比如下定义的一个IEnumerator:
IEnumerator TestCode()
{
    Func1();
    yield return new WaitForSeconds(0.5f);

    Func2();
    yield return new WaitForSeconds(0.5f);

    //...
    FuncN();
    yield return new IEnumeratorObject();
}

public class CompliedIEnumeratorObject : IEnumerator
{
    // execute counter
    int mExecuteCounter = 1;

    public object Current
    {
        get
        {
            return null;
        }
    }

    public bool MoveNext()
    {
        if (mExecuteCounter == 1)
        {
            Func1();
            mExecuteCounter++;

            return true;
        }
        else if (mExecuteCounter == 2)
        {
            if (LastDelayIsOver() == false)
            {
                return true;
            }

            Func2();
            mExecuteCounter++;

            return true;
        }
        //...
        else if (mExecuteCounter == N)
        {
            if (LastDelayIsOver() == false)
            {
                return true;
            }

            FuncN();
            mExecuteCounter++;

            return true;
        }

        return false;
    }

    public void Reset()
    {

    }
}
每个派生自YieldInstruction的对象在编译后会对应一个类似于LastDelayIsOver这样的一个时间跨度判断操作,如果延时成功结束后则进行下一步的操作。
Coroutine::Run()
{
    MoveNext();
}

如此一来,结合C#本身的迭代器特性和Unity中的YieldInstruction对象就可以实现对复杂计算函数的分时操作分解。

4. Unity5.3中的CustomYieldInstruction

通过上述分析,Coroutine的原理已经清楚,如果想实现自定义分时方式的话只需要在YieldInstruction的基础上实现类似于WaitForSeconds的对象即可。但是,在Unity5.3之前的YieldInstrction并没有对引擎外部开放,派生之后并没有什么卵用。不过这一限制在Unity5.3之后的版本中被放开,引擎中添加了一个CustomYieldInstructon的对象来实现自定义的YieldInstruction(延时)对象。而根据上述Coroutine的实现原理,我们也可以直接实现相应的IEnumerator类型的对象来达到同样的目的(注:在Unity5.3之前的版本中通过迭代器也不能实现自定义延时)

public class Wait4Seconds : IEnumerator
{
    float mSeconds = 0.0f;

    float mStartTime = 0.0f;

    public Wait4Seconds(float seconds)
    {
        mSeconds = seconds;
        mStartTime = Time.realtimeSinceStartup;
    }

    public object Current
    {
        get
        {
            return 0;
        }
    }

    public bool MoveNext()
    {
        if (Time.realtimeSinceStartup < mStartTime + mSeconds)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public void Reset()
    {
        return;
    }
}

public class CoroutineTest : MonoBehavior
{
    // Use this for initialization
    void Start()
    {
        StartCoroutine(YieldEnumerator());
    }

    IEnumerator YieldEnumerator()
    {
        Debug.Log("=================================== YieldEnumerator 1: " + Time.realtimeSinceStartup);

        yield return new Wait4Seconds(1.0f);

        Debug.Log("=================================== YieldEnumerator 2: " + Time.realtimeSinceStartup);
    }
}

上述代码在Unity5.3中执行的结果就是第二个Log会比第一个延迟1秒后打印,也即成功的实现了自定义的延时对象。

综述,Unity中的Coroutine是一种很好的特性,在项目实践中需要合理的使用起来,让它发挥更大的作用。

[Unity] 战斗系统学习 14:Switchable 3 1. Bug:协程阻塞游戏主线程 我在测试 Switchable 的时候发现我的过渡并没有达到应有的效果 使用 IEnumerator 函数用于协程 /// <summary> /// 模式过渡:使变量在不同预设值之间切换 /// </summary> /// <param name="mode">预设模式</param> /// <returns></retur 阅读详情

相关推荐

从原理到实践:MIPI D-PHY v1.2的HS-Deskew功能全解析(附时序图详解)

本文深入解析MIPI D-PHY v1.2的HS-Deskew功能,从技术背景到实现机制,详细介绍了其在2.5Gbps高速传输中的关键作用。通过时序图详解和实测数据,帮助工程师理解并应用这一技术,解决信号完整性问题,提升系统可靠性。

weixin_30300225的博客 307

CustomYieldInstruction

         用于挂起协同程序的自定义yield指令的基类。CustomYieldInstruction允许您实现自定义yield指令以暂停协程执行,直到事件发生。在引擎底层,自定义指令只是另一个运行协程。要实现它,请从CustomYieldInstruction类继承 并覆盖keepWaiting属性。保持协程暂停返回true。让coroutine继续执行返回 false。在MonoBeha...

嘿嘿 2204

坐标偏移不一致或瓦块重名的多个OSGB工程的融合

介绍如何使用实景三维小助手OSGBLab对坐标偏移不一致或瓦块重名的多个OSGB工程的融合

OSGBLab的博客 3231

Unity自定义协程——CustomYieldInstruction

Unity的Coroutine非常好用。在很多需要延迟执行的地方,都可以省掉大量功夫。Coroutine的缺点也很明显。CustomYieldInstruction很大程度上可以解决这些缺点。

oyji1992的博客 5550

华为eNSP与VMware虚拟机联动实战:从零搭建仿真网络环境

本文详细介绍了如何从零开始搭建华为eNSP与VMware虚拟机的联动仿真网络环境。通过环境准备、网络适配器配置、eNSP与VMware网络集成、安全策略与NAT配置等步骤,帮助读者掌握构建企业级网络实验场景的关键技术。这种组合特别适合网络工程师进阶训练和华为认证考试准备,实现低成本、高灵活性的网络仿真实验。

weixin_30415113的博客 502

基于STM32环境温湿度监测系统设计

基于STM32环境温湿度监测系统设计1. 实验目的2. 任务和要求3. proteus仿真图4. 实验代码 1. 实验目的 (1) 掌握使用 KEIL MDK5 进行软件开发的具体流程; (2) 掌握硬件电路的设计方法; (3) 掌握 STM32 微控制器的程序设计及代码编写方法。 2. 任务和要求 (1)在 STM32F103 平台下,通过温度传感器 DS18B20 和湿度传感器 DHT11 实现环境温湿度信息的采集; (2)将温度传感器和湿度传感器所采集的数据在 LCD 液晶屏幕上显示出来,超

唯一的博客 2万+

【Unity】协程原理,以及CustomYieldInstruction

 我们知道Unity中开启一个协程是这样的: public class ExampleScript : MonoBehaviour { void Start() { StartCoroutine(DoSomThing()); } public IEnumerator DoSomeThing() { Debug.Log("Start" + Time.realtimeSinceStartup); yield return

unity尧明的博客 848

[STM32]HAL STM32F103 软件I2C读写MPU6050(附DMP库移植)

本文介绍了如何在STM32F103C8T6上通过I2C协议与MPU6050传感器通信并实现DMP功能。首先,使用STM32CubeMX配置项目,设置时钟和I2C接口。然后,编写软件I2C协议的读写函数与MPU6050交互。接着,移植DMP库并进行必要修改,获取加速度计和陀螺仪数据,并将其转换为欧拉角显示在OLED上。最后,利用Keil的逻辑分析仪监测I2C时序。

m0_56841583的博客 2321

【Unity知识点详解】StartCoroutine、CustomYieldInstruction详解

WaitForSeconds是Unity提供的协程暂停方法,除此之外Unity还提供了WaitForEndOfFrame,WaitForSecondsRealtime等暂停方法,大家可以在官网自行查阅。完整的示例如上,启动协程后会先输入日志“Log 111111”,在yield处暂停挂起,当鼠标左键点击后,继续执行输出日志“Log 222222”。除了Unity官方提供的暂停方法,Unity还提供了CustomYieldInstruction基类,可以自定义协程的暂停方法。

Backyard Garden Of Games 2506

如何安装PyFluent

PyFluent 是PyAnsys生态系统的一部分, 允许您在所选的 Python 环境中结合使用 Fluent 与其他 PyAnsys 库和外部 Python 库一起使用。PyFluent 实现了客户端-服务器体系结构。它使用谷歌遥控器 过程调用或 gRPC 接口,用于启动或连接正在运行的 Fluent 进程作为服务器。但是,您只需要与 Python 交互接口。可以使用 PyFluent 以编程方式创建、交互和控制 Fluent 会话,用于创建您自己的自定义工作区。

B_etter_Man的博客 2913

05 - GRACE数据处理-球谐展开与球谐综合

GRACE数据处理-球谐展开和综合前面在介绍GRACE相关理论的时候提到过下面的公式,从公式形式上可以看出,面密度变化和球谐系数(CS)之间是能够相互转换的,那这个转化的过程可以称为球谐展开,简单理解就是将全球网格数据转化为球谐系数的过程。而球谐综合可以看作是球谐展开的逆过程,利用球谐系数计算到全球格网分布。

weixin_43339605的博客 1050

Unity的中自定义协程指令和协程的模拟实现

本文分享Unity的中自定义协程指令和协程的模拟实现 在上一篇文章中, 我们简单分享了Unity和Lua中协程的基本概念和用法, 并将两者做了一些比较. 在这篇文章中, 我们将进一步探索Unity对协程的实现, 并通过自定义协程来猜测和模拟Unity是如何实现协程的. Unity中自定义协程指令 Unity默认提供的WaitForSeconds, WaitForEndOfFrame之类的指令继承于YieldInstruction. 也提供了一些灵活的方式来定义自定义的指令. 官方文档提供了两种实现自定义指令

木头圈 1509

短信验证码逻辑漏洞学习——各种绕过姿势技巧

短信验证码逻辑漏洞学习——各种绕过姿势技巧

记录并分享学习安全的知识点.. 2555

Unity开发问题记录体系构建与高频场景解析

在游戏开发与软件工程实践中,系统化的问题记录与知识管理是提升团队协作效率和项目稳定性的核心工程实践。其原理在于将零散的故障排查经验转化为结构化、可检索、可复用的技术资产,通过标准化的记录模板(如问题标题、复现步骤、根本原因、解决方案)形成内部知识库。这一实践的技术价值在于显著降低同类问题的重复排查成本,加速新成员融入,并沉淀团队在特定技术栈(如Unity引擎)上的深度认知。常见的应用场景包括编辑器卡顿、运行时内存泄漏、跨平台构建异常等高频开发痛点。本文以Unity引擎为具体载体,深入探讨了如何构建有效的问题

weixin_33985507的博客 359

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Unity游戏开发从入门到精通:构建技能金字塔与实战路径

游戏引擎是现代游戏与交互式内容创作的核心工具,它通过提供一套完整的开发框架,将图形渲染、物理模拟、音频处理等底层技术封装为易于使用的接口。其工作原理基于实体-组件架构,开发者通过组合不同的功能模块来构建复杂的游戏逻辑与交互体验。这一技术价值在于大幅降低了3D内容创作的门槛,提升了开发效率,使得个人开发者和小型团队也能制作出高质量的作品。在应用场景上,游戏引擎不仅用于开发各类电子游戏,还广泛应用于虚拟现实、数字孪生、建筑可视化及影视动画预览等领域。本文聚焦于Unity引擎,深入探讨其核心工作流、资源管理、性能

weixin_30274627的博客 468

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Unity游戏多语言化实战:使用XUnity.AutoTranslator实现非侵入式本地化

游戏本地化是游戏开发中实现全球化发行的关键技术,其核心原理在于将游戏内文本资源与特定语言版本进行映射与替换。传统方案往往需要对源代码进行大量侵入式修改,而运行时文本拦截技术则提供了一种更灵活的解决方案。该技术通过挂钩底层文本渲染函数,在不修改原有资产和逻辑的前提下,动态捕获并替换显示字符串,其技术价值在于能极大降低存量项目的本地化改造成本,并支持处理程序动态生成的文本。在Unity引擎中,这一技术常被应用于中大型项目的快速多语言支持、为玩家社区翻译模组提供基础框架等场景。本文以热门的XUnity.AutoT

cuml0912的博客 480

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

全栈游戏开发AI引导平台:架构设计与工程实践

在游戏开发领域,全栈开发要求开发者从前端渲染、后端逻辑到部署运维均有涉猎,学习曲线陡峭。微服务架构与AI Agent的结合,为解决这一难题提供了新的技术路径。通过构建领域知识图谱和设计多模态AI智能体,系统能够理解游戏开发的特殊语境,如状态同步、游戏循环等核心概念,并为开发者提供从代码生成、架构咨询到问题诊断的全流程智能辅助。这种技术方案的核心价值在于显著降低开发者的学习与协作成本,将开发者从重复性工作中解放,更专注于创意实现。本文以游戏开发AI引导平台为例,深入解析了其以Neo4j图数据库和分层AI响应系

weixin_33709590的博客 518

浅谈Unity协程的工作机制

Unity协程的工作机制

R3333355726856的博客 1752

Unity 协程探究

一、官方手册中的描述 1、Manual/Coroutines 函数在调用时, “从调用到返回” 都发生在一帧之内,想要处理 “随时间推移进行的事务”, 相比Update,使用协程来执行此类任务会更方便。 协程在创建时,通常是一个“返回值类型 为 IEnumerator”、“函数体中包含 yield return 语句 ” 的函数。 yiled return 可以暂停协程的执行,并在恰当时...

NRatel的博客 3140

Unity3D教程自定义YieldInstruction

(1) 我学院:Unity3D教程自定义YieldInstruction https://wenku.baidu.com/view/24bbad1dbb4cf7ec4bfed067.html (2) CustomYieldInstruction https://docs.unity3d.com/ScriptReference/CustomYieldInstruction.html Description Base class for custom yield instructions to su

kuangben2000的博客 947

Unity基础篇:协程(协同程序)的概括(StartCoroutine 和yield return和StopCoroutine )

MonoBehaviour.StartCoroutine 开始协同程序 public Coroutine StartCoroutine(IEnumerator routine); 一个协同程序在执行过程中,可以在任意位置使用yield语句。yield的返回值控制何时恢复协同程序向下执行。协同程序在对象自有帧执行过程中堪称优秀。协同程序在性能上没有更多的开销。StartCoroutine函数是立...

设计师养成记 4万+

Unity 中的 async-await 支持YieldInstruction

本文都是参考c一个开源的项目:Unity3dAsyncAwaitUtil/releases 之前写的文章可以参考:Unity 中的 async-await 关键字解析 本文要实现的就是:await 可以直接接 协程 yield return 的东西 此处就拿 WaitForSecond 举例子: void Start() { ForIEnumator(); } async void ForIEnumator() { await new WaitForSeconds(1f); De

东北砍王的栖息地 1249

扩展Coroutine:自定义YieldInstruction

实现自定义YieldInstruction的功能的一个方法介绍,以及演示实例。

景夫的博客 7578

Unity协程认知

协程使用之前请注意以下几点:1.协程只需要调用一次,只需要调用一次,只需要调用一次 !2.yield return表示挂起,挂起的意思是协程里的变量会被保留,直到达到return的条件才会执行return以下的代码片段。举个例子: yield return new WaitForSeconds(0.9f);//执行到这儿,等待0.9s,继续执行这句代码以下的代码片段3.开启协程的方式:a.Sta

LM_MT的博客 1870

unity协程_Unity中的Yield Return是个什么鬼?

写完以后发现了这篇博文,看完觉得自己太浅薄了。Coroutine,你究竟干了什么?_tkokof1的专栏-优快云博客​blog.csdn.net好多年没怎么用Unity,回过头来一看已经2020版本了,哈哈,当年最早还是用1.7版本,一帮菜鸟级码农在出租屋里写着大量if嵌套的丑陋代码,后来自己跟着一位大神开始C#+Unity之不归路,当时还是3.5版本,而自己做了个小项目的时候升到了4.3。真是...

weixin_39723010的博客 1097

Unity中的协程

1. 协程(Coroutines) 是一种比线程更加轻量级的存在,也被称为用户态线程 2. 一个进程可以拥有多个线程,一个线程可以拥有多个协程 3. 协程并不会增加线程,它在线程中运行,通过分时复用的方式运行多个协程,其切换代价比线程切换代价小很多 4. 协程并非独立与线程,所以协程会因为线程的阻塞而阻塞,往往主线程阻塞时,其协程都会因为阻塞而得不到调度 5. 因此在协程中不能调用导致线程阻塞的操作,最好与异步IO相结合,比如打印,读取,socket接口等。

KamikazePilot的博客 1703
上一篇: Unity Notes之配置文件基于内容的差异化更新
下一篇: Unity Notes之屏幕触点轨迹的平滑
BugRunner
博客等级 码龄17年 791粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值