当前位置: 首页 > news >正文

浅谈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 中&#xff0c;协程&#xff08;Coroutine&#xff09;是一种非常常用的机制&#xff0c;用于非阻塞地处理需要跨越多个帧、等待某些条件或延迟一段时间才能完成的任务。Unity 的协程通过 C# 的 IEnumerator 和 yield return 实现&#xff0…...

数学建模_数据预处理流程(全)

数据预处理整体流程图 一般数据预处理流程 处理缺失值&#xff1a;填补或删除缺失值。处理异常值&#xff1a;检测并处理异常值。数据编码&#xff1a;将分类变量进行标签编码或独热编码。数据标准化/归一化&#xff1a;对数据进行标准化或归一化处理。连续变量离散化&#xff…...

深入解析Flink SQL:基本概念与高级应用

深入解析Flink SQL&#xff1a;基本概念与高级应用 目录 深入解析Flink SQL&#xff1a;基本概念与高级应用 引言 一、Flink SQL概述 1. Flink SQL的定义 2. Flink SQL与传统SQL的区别 二、Flink SQL的核心特性 1. 事件时间和处理时间 2. 窗口操作 3. 连接和聚合操作 …...

计算机的发展史和基本结构

好久不见&#xff0c;粉粉们&#xff0c;我是#Y清墨。今天来分享一下最近学习做的笔记。 计算机发展史和四代计算机概述 阶段 年代 电子元件 运算速度&#xff08;每秒/次&#xff09; 第一代 1946-1958 真空电子管 数千至数万 第二代 1958-1964 晶体管 几十万至百万…...

VSTO常见的异常

在开发和使用 VSTO&#xff08;Visual Studio Tools for Office&#xff09;插件时&#xff0c;常见的异常主要涉及到与 Microsoft Office 应用程序&#xff08;如 Excel、Word、Outlook 等&#xff09;的交互、环境配置和 COM 组件的使用。以下是一些常见的异常及其可能的原因…...

【H2O2|全栈】关于HTML(3)HTML基础(二)

HTML相关知识 目录 HTML相关知识 前言 准备工作 标签的具体分类&#xff08;二&#xff09; 本文中的标签在什么位置使用&#xff1f; 本期前置知识点 超文本 超文本引用和源属性 图片标签 锚链接 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、什么是编译器 定义&#xff1a;编译器就是个将当前语言…...

基于EPS32C3电脑远程开机模块设计

基于EPS32C3电脑远程开机模块设计 前言 缘起&#xff0c;手头资料太多了&#xff0c;所以想组一台NAS放在家里存储数据。在咸鱼淘了一套J3160主板加机箱&#xff0c;加上几块硬盘组建NAS。 对于NAS&#xff0c;我的需求是不用的时候关机(节省功耗)&#xff0c;要用的时候开机…...

深度解析 Netty 性能卓越的背后原因

一、引言 在当今数字化时代&#xff0c;构建高性能、高可靠的网络应用成为了技术领域的关键需求。Netty 作为一款备受推崇的网络应用框架&#xff0c;以其出色的性能在众多框架中脱颖而出。深入探究 Netty 性能卓越的原因&#xff0c;不仅能够帮助开发者更好地理解和运用这一框…...

虚幻引擎(Unreal Engine)技术使得《黑神话悟空传》大火,现在重视C++的开始吃香了,JAVA,Go,Unity都不能和C++相媲美!

虚幻引擎&#xff08;Unreal Engine&#xff09;火了黑神话游戏。 往后&#xff0c;会有大批量的公司开始模仿这个赛道&#xff01; C 的虚拟引擎技术通常指的是使用 C 语言开发的游戏引擎&#xff0c;如虚幻引擎&#xff08;Unreal Engine&#xff09;等。以下是对 C 虚拟引…...

华为-2022-测试面试题

文章目录 一、源数组a&#xff0c;将a中所有元素乘以2之后组成一个新数组&#xff0c;则这个新数组就叫双倍数组&#xff0c;给你一个数组a&#xff0c;判断它是不是双倍数组&#xff0c;如果是则输出源数组&#xff0c;不是则输出空数组。二、如果想把一个文件移动到另一个文件…...

Linux-(系统启动、用户管理)

目录 前言 关机&重启命令 基本介绍 注意细节 用户登录和注销 注意&#xff1a; 用户管理 基本介绍 添加用户 指定/修改密码 删除用户 查询用户信息 切换用户 查看当前用户登录用户 用户组 新增组 删除组 查看所有组 修改用户所属组 创建用户时指定用户…...

机器学习:opencv--图像形态学

目录 前言 一、常用形态学操作 二、腐蚀和膨胀 1.图像腐蚀 2.图形膨胀 三、开运算和闭运算 1.开运算 2.闭运算 四、顶帽和黑帽 1.顶帽 2.黑帽 五、梯度运算 总结 前言 图像形态学是一种用于处理和分析图像形状和结构的技术。 一、常用形态学操作 膨胀&#xff08…...

网络基础入门指南(一)

前言 在这个高度互联的世界里&#xff0c;互联网已成为日常生活不可或缺的一部分。然而&#xff0c;对于许多人来说&#xff0c;网络是如何工作的仍然是个谜。本文旨在为那些对网络基础知识感兴趣的朋友提供一个简单的介绍&#xff0c;帮助大家更好地理解互联网的基本原理和技…...

【项目】云备份

云备份 云备份概述框架 功能演示服务端客户端 公共模块文件操作模块目录操作模块 服务端模块功能划分功能细分模块数据管理热点管理 客户端模块功能划分功能细分模块数据管理目录检查文件备份 云备份 概述 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中。…...

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主机&#xff08;主&#xff09;配置文件 vi /etc/keepalived…...

二分查找题总结

二分查找题总结 hot100搜索插入位置搜索二维矩阵在排序数组中查找元素的第一个和最后一个位置搜索旋转排序数组寻找旋转排序数组中的最小值寻找两个正序数组的中位数 hot100 搜索插入位置 题目链接&#xff1a; 35.搜索插入位置 代码&#xff1a; class Solution {public in…...

仕考网:公务员面试流程介绍

通知进面信息——资格审查——面试签到——抽签候考 面试形式&#xff1a; 面试分为结构化和无领导小组两种形式 1.在结构化面试中&#xff0c;当轮到某位考生时&#xff0c;引导员将在候考室宣布其编号&#xff0c;随后考生跟随引导人员前往考场入口。考生在开始考试时需回…...

(十五)SpringCloudAlibaba-Sentinel持久化到Nacos

前言 在前面我们已经将Sentinel配置的规则持久化到系统的文件中。本章节我们将Sentinel持久化到Nacos中; 传送门(Sentinel数据持久化到文件)https://blog.csdn.net/weixin_45876411/article/details/140742963 默认情况下 Sentinel 只能接收到 Nacos 推送的消息&#xff0c;但…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...