浅谈Unity协程的工作机制
一. 什么是协程
协程概述
在 Unity 中,协程(Coroutine)是一种非常常用的机制,用于非阻塞地处理需要跨越多个帧、等待某些条件或延迟一段时间才能完成的任务。Unity 的协程通过 C# 的 IEnumerator 和 yield return 实现,使得你可以在游戏主线程中以一种简洁的方式执行异步操作,而不需要使用复杂的多线程或回调。
协程允许你暂停代码的执行,并在稍后的某个时间点恢复执行。适合处理游一些常见需求,比如:
1延迟一定时间后执行代码。
2等待某个条件(如动画完成、玩家输入)后继续执行。
3 逐帧执行任务,而不是一次性完成,避免阻塞主线程。
下面一些示例
void Start()
{// 启动协程StartCoroutine(MyCoroutine());Debug.Log("因为是协程,所以不会阻塞打印");
}IEnumerator MyCoroutine()
{Debug.Log("Before waiting");// 暂停协程 2 秒yield return new WaitForSeconds(2);Debug.Log("After waiting 2 seconds");
}
执行流程:
当 StartCoroutine(MyCoroutine()) 被调用时,MyCoroutine 方法开始执行,先输出 "Before waiting".
在遇到yield return时会立即返回控制权给上级调用者,这里上级调用者是主线程,打印"因为是协程,所以不会阻塞打印",并且执行到这里的状态会被保存下来,以便条件(这里的条件是等待两秒)完成后继续向下执行.
2 秒后,协程恢复,继续执行,输出 "After waiting 2 seconds"。
yield return 的使用
协程中的 yield return 是关键,它决定了协程什么时候暂停以及在什么条件下恢复。
yield return null://暂停协程直到下一帧。它相当于让协程在下一帧继续执行。
yield return new WaitForSeconds(x)://暂停协程 x 秒后继续执行。
yield return new WaitUntil(predicate)://等待直到某个条件为 true 时继续执行。
如yield return new WaitUntil(() => someCondition == true); // 等待某个条件成立
yield return new WaitWhile(predicate)://等待直到某个条件为 false 时继续执行。
yield return StartCoroutine(otherCoroutine)://等待另一个协程执行完毕后再继续执行当前协程。
yield return WaitForEndOfFrame():
//暂停协程执行,直到当前帧的渲染完成。可以在本帧所有操作完成后再进行一些后处理。
yield return WaitForFixedUpdate()
//暂停协程执行,直到下一次物理更新(即 FixedUpdate 调用)发生时继续。常用于与物理更新频率保持同步的操作。
常见用例
1. 延迟执行任务
协程经常被用来在一段时间后执行某个任务。以下是一个延迟 3 秒后执行的例子:
IEnumerator DelayedAction()
{yield return new WaitForSeconds(3); // 等待 3 秒Debug.Log("This happens after 3 seconds");
}
2. 等待玩家输入
协程可以暂停代码执行,直到玩家做出某些输入,比如按下某个键:
IEnumerator WaitForPlayerInput()
{Debug.Log("Waiting for player to press Space");yield return new WaitUntil(() => Input.GetKeyDown(KeyCode.Space)); // 等待玩家按下空格键Debug.Log("Player pressed Space");
}
3. 多协程依赖执行
你可以让一个协程等待另一个协程执行完成后再继续。例如,下面的例子展示了如何等待两个协程执行完成后再执行后续逻辑:
IEnumerator Sequence()
{yield return StartCoroutine(Task1());yield return StartCoroutine(Task2());Debug.Log("Both tasks completed");
}IEnumerator Task1()
{Debug.Log("Task 1 started");yield return new WaitForSeconds(2);Debug.Log("Task 1 completed");
}IEnumerator Task2()
{Debug.Log("Task 2 started");yield return new WaitForSeconds(1);Debug.Log("Task 2 completed");
}
4. 逐帧处理任务
有时我们希望任务能在多个帧内逐渐完成,而不是一次性完成以避免卡顿。协程可以轻松实现这一点:
IEnumerator DoTaskOverTime()
{for (int i = 0; i < 10; i++){Debug.Log("Processing step: " + i);yield return null; // 等待到下一帧再继续执行}
}
协程的注意事项
协程不是真正的多线程:
虽然协程可以暂停执行,并在之后恢复,但它们始终在主线程中运行。也就是说,协程不会并发执行,只是通过暂停和恢复的方式避免了阻塞主线程的情况。
Unity 的协程是始终在主线程上运行的,并不涉及多线程处理。但因为协程使用了 yield return 进行暂停,所以它可以让其他代码继续执行,而不会阻塞主线程的运行。这种非阻塞的行为有时会让人误以为协程运行在另一个线程上,但实际上它仍然依赖主线程的调度。
假设你启动了一个协程,在协程中等待 2 秒后打印一段话,而协程下面是一个耗时的方法。
void Start()
{// 启动协程StartCoroutine(MyCoroutine());// 耗时操作HeavyOperation();
}IEnumerator MyCoroutine()
{Debug.Log("Before waiting");// 暂停协程 2 秒yield return new WaitForSeconds(2);Debug.Log("After waiting 2 seconds");
}void HeavyOperation()
{// 模拟一个非常耗时的操作,比如计算密集型任务Debug.Log("Heavy operation started");for (int i = 0; i < 1000000000; i++) { } // 假设这是一个耗时的操作Debug.Log("Heavy operation finished");
}
这里就会出现一个问题:
因为 HeavyOperation() 占用了大量的 CPU 时间,主线程处理其他任务(包括更新协程的进度)可能会延迟。这意味着即使 2 秒时间已经过去了,但如果 HeavyOperation() 还没执行完,协程中的 "After waiting 2 seconds" 也会推迟打印。
当协程遇到 yield return 时,执行会挂起并交出控制权,让上级调用者(通常是 Unity 的主线程)继续执行其他任务。然后,当条件满足后,协程会恢复执行,继续从挂起的地方往下执行。
注意这个条件满足之后,说的不清晰,让我们进一步说清楚协程恢复的时机:当协程的条件满足时(例如等待时间结束),Unity 不会立即打断主线程的操作。协程的恢复是在主线程的当前帧所有任务结束之后,才会被调度执行。
上面的例子是等待两秒,但是下面正在执行一个需要大量cpu的操作,两秒已经过去了还没有执行完,Unity并不会打断这个操作,所以这个等待秒数的行为并不是绝对靠谱的.
这是协程与多线程的关键区别之一。协程不会在任何时间打断主线程的工作,而是等到主线程“空闲”时(例如,一帧的任务全部完成),才会恢复执行。
在大多数情况下,协程的等待时间(比如 WaitForSeconds(2) 等待 2 秒)是靠谱的,但确实存在一些特殊情况可能导致时间的精确度不如帧数那样可靠。具体来说,帧是更为精确和可控的,而秒的等待可能会受到主线程的负载、性能瓶颈等因素的影响。
在 Unity 中,协程的恢复是基于帧更新的。Unity 的主线程在每一帧都会处理脚本、物理、渲染等任务,如果主线程负载过重或者有一些耗时操作,帧率可能会降低(例如从 60FPS 降到 30FPS),进而影响时间的精度。
协程的生命周期:
如果启动协程的 MonoBehaviour 被销毁或禁用,协程会自动停止。因此,协程的生命周期依赖于启动它的对象。
如果你希望手动停止协程,可以使用 StopCoroutine() 方法。
Coroutine myCoroutine;void Start()
{myCoroutine = StartCoroutine(MyCoroutine());
}void StopMyCoroutine()
{if (myCoroutine != null){StopCoroutine(myCoroutine);}
}
YieldInstruction
YieldInstruction 类是一个抽象基类,用于控制协程的执行。通过使用 yield 关键字与 YieldInstruction 子类(如 WaitForSeconds, WaitForEndOfFrame, WaitForFixedUpdate 等)配合,可以让协程暂停一段时间,或者直到满足某些条件才继续执行。
CustomYieldInstruction
继承自 YieldInstruction 的类
CustomYieldInstruction 是一个可继承的类,允许开发者自定义协程的等待条件。通过重写 keepWaiting 属性,可以定义更复杂的逻辑,来控制协程何时继续执行
通过继承 CustomYieldInstruction,你可以实现复杂的等待条件。
public class WaitForCondition : CustomYieldInstruction
{private Func<bool> condition;public WaitForCondition(Func<bool> condition){this.condition = condition;}public override bool keepWaiting{get { return !condition(); }}
}// 使用自定义的协程等待条件
IEnumerator CustomWaitExample()
{Debug.Log("Waiting for custom condition...");yield return new WaitForCondition(() => Time.time > 5);Debug.Log("Condition met, continuing...");
}
相关文章:
浅谈Unity协程的工作机制
一. 什么是协程 协程概述 在 Unity 中,协程(Coroutine)是一种非常常用的机制,用于非阻塞地处理需要跨越多个帧、等待某些条件或延迟一段时间才能完成的任务。Unity 的协程通过 C# 的 IEnumerator 和 yield return 实现࿰…...
数学建模_数据预处理流程(全)
数据预处理整体流程图 一般数据预处理流程 处理缺失值:填补或删除缺失值。处理异常值:检测并处理异常值。数据编码:将分类变量进行标签编码或独热编码。数据标准化/归一化:对数据进行标准化或归一化处理。连续变量离散化ÿ…...
深入解析Flink SQL:基本概念与高级应用
深入解析Flink SQL:基本概念与高级应用 目录 深入解析Flink SQL:基本概念与高级应用 引言 一、Flink SQL概述 1. Flink SQL的定义 2. Flink SQL与传统SQL的区别 二、Flink SQL的核心特性 1. 事件时间和处理时间 2. 窗口操作 3. 连接和聚合操作 …...
计算机的发展史和基本结构
好久不见,粉粉们,我是#Y清墨。今天来分享一下最近学习做的笔记。 计算机发展史和四代计算机概述 阶段 年代 电子元件 运算速度(每秒/次) 第一代 1946-1958 真空电子管 数千至数万 第二代 1958-1964 晶体管 几十万至百万…...
VSTO常见的异常
在开发和使用 VSTO(Visual Studio Tools for Office)插件时,常见的异常主要涉及到与 Microsoft Office 应用程序(如 Excel、Word、Outlook 等)的交互、环境配置和 COM 组件的使用。以下是一些常见的异常及其可能的原因…...
【H2O2|全栈】关于HTML(3)HTML基础(二)
HTML相关知识 目录 HTML相关知识 前言 准备工作 标签的具体分类(二) 本文中的标签在什么位置使用? 本期前置知识点 超文本 超文本引用和源属性 图片标签 锚链接 iframe 锚点 预告和回顾 后话 前言 本系列博客将分享HTML相关…...
前端AST
前端AST 1、什么是编译器2、什么是AST3、编译器的基本思路3.1 词法分析3.2 语法分析3.3 代码转换3.4 代码生成3.5 完整链路 4、一个简单的编译器的实现4.1 词法分析4.2 语法分析4.3 代码转换4.4 代码生成4.5 完整流程 1、什么是编译器 定义:编译器就是个将当前语言…...
基于EPS32C3电脑远程开机模块设计
基于EPS32C3电脑远程开机模块设计 前言 缘起,手头资料太多了,所以想组一台NAS放在家里存储数据。在咸鱼淘了一套J3160主板加机箱,加上几块硬盘组建NAS。 对于NAS,我的需求是不用的时候关机(节省功耗),要用的时候开机…...
深度解析 Netty 性能卓越的背后原因
一、引言 在当今数字化时代,构建高性能、高可靠的网络应用成为了技术领域的关键需求。Netty 作为一款备受推崇的网络应用框架,以其出色的性能在众多框架中脱颖而出。深入探究 Netty 性能卓越的原因,不仅能够帮助开发者更好地理解和运用这一框…...
虚幻引擎(Unreal Engine)技术使得《黑神话悟空传》大火,现在重视C++的开始吃香了,JAVA,Go,Unity都不能和C++相媲美!
虚幻引擎(Unreal Engine)火了黑神话游戏。 往后,会有大批量的公司开始模仿这个赛道! C 的虚拟引擎技术通常指的是使用 C 语言开发的游戏引擎,如虚幻引擎(Unreal Engine)等。以下是对 C 虚拟引…...
华为-2022-测试面试题
文章目录 一、源数组a,将a中所有元素乘以2之后组成一个新数组,则这个新数组就叫双倍数组,给你一个数组a,判断它是不是双倍数组,如果是则输出源数组,不是则输出空数组。二、如果想把一个文件移动到另一个文件…...
Linux-(系统启动、用户管理)
目录 前言 关机&重启命令 基本介绍 注意细节 用户登录和注销 注意: 用户管理 基本介绍 添加用户 指定/修改密码 删除用户 查询用户信息 切换用户 查看当前用户登录用户 用户组 新增组 删除组 查看所有组 修改用户所属组 创建用户时指定用户…...
机器学习:opencv--图像形态学
目录 前言 一、常用形态学操作 二、腐蚀和膨胀 1.图像腐蚀 2.图形膨胀 三、开运算和闭运算 1.开运算 2.闭运算 四、顶帽和黑帽 1.顶帽 2.黑帽 五、梯度运算 总结 前言 图像形态学是一种用于处理和分析图像形状和结构的技术。 一、常用形态学操作 膨胀(…...
网络基础入门指南(一)
前言 在这个高度互联的世界里,互联网已成为日常生活不可或缺的一部分。然而,对于许多人来说,网络是如何工作的仍然是个谜。本文旨在为那些对网络基础知识感兴趣的朋友提供一个简单的介绍,帮助大家更好地理解互联网的基本原理和技…...
【项目】云备份
云备份 云备份概述框架 功能演示服务端客户端 公共模块文件操作模块目录操作模块 服务端模块功能划分功能细分模块数据管理热点管理 客户端模块功能划分功能细分模块数据管理目录检查文件备份 云备份 概述 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。…...
WebGL系列教程二(环境搭建及初始化Shader)
目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…...
keepalive和nginx高可用集群
keepalived 和 nginx 高可用集群搭建 主备模式 zyj86主机和zyj87主机安装nginx和keepalived yum install nginx keepalived -y systemctl enable --now nginx.service keepalived.service主调度器配置 编辑zyj86主机(主)配置文件 vi /etc/keepalived…...
二分查找题总结
二分查找题总结 hot100搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置搜索旋转排序数组寻找旋转排序数组中的最小值寻找两个正序数组的中位数 hot100 搜索插入位置 题目链接: 35.搜索插入位置 代码: class Solution {public in…...
仕考网:公务员面试流程介绍
通知进面信息——资格审查——面试签到——抽签候考 面试形式: 面试分为结构化和无领导小组两种形式 1.在结构化面试中,当轮到某位考生时,引导员将在候考室宣布其编号,随后考生跟随引导人员前往考场入口。考生在开始考试时需回…...
(十五)SpringCloudAlibaba-Sentinel持久化到Nacos
前言 在前面我们已经将Sentinel配置的规则持久化到系统的文件中。本章节我们将Sentinel持久化到Nacos中; 传送门(Sentinel数据持久化到文件)https://blog.csdn.net/weixin_45876411/article/details/140742963 默认情况下 Sentinel 只能接收到 Nacos 推送的消息,但…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
