Unity——协程(Coroutine)

这篇具有很好参考价值的文章主要介绍了Unity——协程(Coroutine)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文为问GPT所得


一、在Unity中,协程到底是个啥

在Unity中,协程(Coroutine)是一种特殊的函数,用于在一段时间内暂停执行,并在稍后的时间点继续执行。通常情况下,我们在代码中通过调用协程来实现一些需要延迟执行或需要分步处理的任务

协程在Unity中有如下特点:

  1.  可以在一帧中的不同时间点暂停和恢复执行,而不需要等待整个帧的结束。
  2. 可以方便地实现延迟执行,例如在几秒后播放音效或执行一个动画。
  3. 可以使用协程来实现复杂的异步任务,而不需要使用回调函数或依赖于线程。

使用协程可以简化代码逻辑,使得任务的实现更加清晰和可读。在Unity中,可以使用C#的yield关键字来定义和控制协程的执行流程。例如,可以使用yield return语句来暂停执行,并在满足某个条件后继续执行。

以下是一个简单的示例,演示了一个协程的用法:

using UnityEngine;
using System.Collections;

public class CoroutineExample : MonoBehaviour
{
    IEnumerator Start()
    {
        Debug.Log("协程开始");
        yield return new WaitForSeconds(2f);
        Debug.Log("等待了2秒后继续执行");
        yield return new WaitForSeconds(1f);
        Debug.Log("又等待了1秒后继续执行");
    }
}

在上面的示例中,Start方法是一个协程。通过yield return语句,我们在协程中暂停执行了2秒和1秒,并在等待结束后继续执行。当我们在MonoBehaviour中启动这个协程时,它将按照我们定义的流程执行。

二、协程的原理

协程(Coroutine)是一种轻量级的并发编程技术,它可以在单个线程内实现多个执行流的并发操作。协程的原理主要涉及到两个概念:上下文切换和yield(让出执行权)

  1.  上下文切换:协程通过上下文切换来实现在不同协程之间的切换执行。当一个协程执行到某个位置时,它会保存当前的上下文(包括程序计数器、堆栈指针等信息),然后切换到下一个要执行的协程,恢复其保存的上下文,并从切换位置继续执行。
  2.  yield(让出执行权):yield 是协程中的一个关键操作,它可以让协程主动让出执行权,将控制权交给其他协程。当一个协程执行到 yield 语句时,它会暂停执行,并将结果返回给调度器(或其他协程),同时保存当前的上下文。接下来,调度器会选择另一个协程来执行,将保存的上下文恢复,然后继续执行。

通过上下文切换和yield操作,协程能够实现非抢占式的协作式调度,也就是协程主动地让出执行权,而不是由操作系统或调度器去强制切换。这样可以更灵活地控制协程的执行顺序和并发度,避免资源竞争和锁机制带来的开销。

在实现上,协程可以由编程语言提供的原生支持(如Python的asyncio、Go的goroutine),也可以通过生成器(Generator)和状态机等手动实现。不同的语言和库对协程的具体实现方式可能略有不同,但核心原理基本相似。

总结起来,协程通过上下文切换和yield操作实现协作式调度,使得在单个线程内能够实现多个执行流的并发操作,提高了程序的并发性和灵活性。

三、程和线程的区别

当谈论协程和线程的区别时,主要有以下几点:

  1. 执行方式:线程是由操作系统调度的最小执行单位,它在操作系统的控制下运行,可以并行执行。协程是由程序控制的执行单位,它由程序自己在协程调度器的管理下运行,可以通过协作方式实现并发。
  2. 并发性:线程可以在多个 CPU 核心上并行执行,利用多核优势提高计算效率。而协程通常在单个线程中运行,通过在不同协程间的切换实现并发,无法利用多核优势。 
  3. 内存和资源消耗:线程在创建时需要分配独立的内存空间,包括堆栈等资源,同时线程切换时需要保存和恢复上下文,消耗较多的内存和资源。协程则可以在一个线程中存在多个,共享同一堆栈空间,切换时只需要保存和恢复少量上下文,消耗较少的内存和资源。
  4. 同步与通信:在线程中,由于存在资源竞争,需要使用锁机制等同步手段来保护共享资源,避免数据冲突。协程通过显式地让出执行权和恢复执行权来避免资源竞争,并通过通道(channel)等机制进行协程间的通信。

总的来说,线程适合于 CPU 密集型任务,可以利用多核优势提高计算效率,但在高并发环境下可能存在资源竞争的问题。协程适合于 IO 密集型任务,可以通过协作方式实现高并发,但无法利用多核优势。根据具体的应用场景和需求,选择合适的并发模型可以提高程序的性能和效率。

四、协程的函数与规则 

PartⅠ

  1. 协程函数的声明:协程函数必须是返回类型为 IEnumerator 的方法。在C#中,我们用特殊的语法来声明协程函数,即在方法签名前面添加关键字 `IEnumerator`。
  2. yield return 语句:协程函数中通过使用 `yield return` 语句来控制流程。这个语句可以暂停执行并返回一个值,它的值将在协程继续执行时被使用。可以使用多种类型的 `yield return` 语句,包括 `yield return null`、`yield return new WaitForSeconds(delayTime)` 或者自定义的对象。
  3. 每帧执行:协程函数在不同的时间点暂停和继续。Unity自动管理协程的迭代器,并在每帧中递进执行。这意味着协程函数的每个 yield return 语句将在下一帧或指定的延迟时间后执行。例如,`yield return null` 将会在下一帧继续执行。
  4. 协程生命周期:协程函数的生命周期与启动它的 `MonoBehaviour` 组件密切相关。当 `MonoBehaviour` 组件启用时,协程开始执行;当组件禁用或销毁时,协程停止执行。这意味着协程函数可以在对象激活期间执行,也可以在对象非激活期间暂停执行。
  5. 停止和恢复协程:我们可以使用 `StopCoroutine()` 或 `StopAllCoroutines()` 来停止正在运行的协程。使用 `yield break` 语句可以提前结束协程的执行。如果需要重新启动协程,只需再次调用它即可。

PartⅡ

6. 启动协程:要启动协程,可以使用 `StartCoroutine()` 方法。这个方法可以接受一个协程函数作为参数,并在每帧中执行该函数。可以将协程函数作为参数传递给 `StartCoroutine()`,如下所示:

   StartCoroutine(MyCoroutineFunction());

   注意,返回的 `Coroutine` 对象可以用于控制和监视协程的状态,如停止或暂停。

7. 停止和暂停协程:通过调用 `StopCoroutine()` 或 `StopAllCoroutines()` 方法可以停止正在运行的协程。可以将协程函数作为参数传递给 `StopCoroutine()`,如下所示:

   StopCoroutine(MyCoroutineFunction());

   你还可以使用 `yield break` 语句来提前结束协程的执行,使其立即返回。

8. 嵌套协程:你可以在一个协程中启动另一个协程。这被称为嵌套协程。嵌套协程可以帮助你组织和管理复杂的逻辑。要启动嵌套协程,可以使用 `StartCoroutine()` 方法,并将协程函数作为参数传递给它。

IEnumerator MyCoroutineA()
   {
       Debug.Log("协程 A 开始执行");
       yield return StartCoroutine(MyCoroutineB());
       Debug.Log("协程 A 继续执行");
   }

   IEnumerator MyCoroutineB()
   {
       Debug.Log("协程 B 开始执行");
       yield return new WaitForSeconds(2f);
       Debug.Log("协程 B 完成执行");
   }

   在上面的示例中,协程函数 `MyCoroutineA()` 启动了嵌套的协程 `MyCoroutineB()`。协程 `MyCoroutineA()` 在 `MyCoroutineB()` 完成执行后继续执行。

PartⅢ

9. WaitForSeconds和WaitForSecondsRealtime:可以使用 `yield return new WaitForSeconds(delayTime)` 或 `yield return new WaitForSecondsRealtime(delayTime)` 来暂停协程的执行一段指定的时间。其中 `WaitForSeconds` 以游戏时间为基准计时,而 `WaitForSecondsRealtime` 以实际时间为基准计时。这两个等待语句对于实现延迟执行或定时操作非常有用。

  IEnumerator MyCoroutine()
   {
       Debug.Log("等待2秒");
       yield return new WaitForSeconds(2f);
       Debug.Log("继续执行");
   }

10. 异常处理:协程中的异常处理可以通过使用 try-catch 语句来实现。异常会中断协程的执行,并且可以捕获和处理异常。在捕获到异常后,你可以决定是继续执行协程还是提前结束协程。

   IEnumerator MyCoroutine()
    {
        try
        {
            // 协程逻辑
        }
        catch (Exception ex)
        {
            // 处理异常
        }

11. 返回值:协程函数可以返回一个值,这个值可以通过 `yield return` 语句返回。这个返回值可以在调用 `StartCoroutine()` 后使用返回的 `Coroutine` 对象的 `Coroutine.Current` 属性访问。 

   IEnumerator MyCoroutine()
    {
        yield return 10;
    }

    void Start()
    {
        Coroutine coroutine = StartCoroutine(MyCoroutine());
        int value = (int)coroutine.Current;
    }

12. 停止全部协程:在某些情况下,你可能需要停止场景中所有正在运行的协程。可以使用 `StopAllCoroutines()` 方法来停止全部协程的执行。

   void StopAllCoroutines()
    {
        StopAllCoroutines();
    }

PartⅣ

13. 异步操作和回调:协程可以用于实现异步操作和回调。通过使用 `yield` 和 `yield return` 语句,可以在协程中等待异步操作完成,然后根据需要执行相应的逻辑。这样可以避免使用回调函数,使代码更加清晰和可读。

   IEnumerator MyCoroutine()
   {
       // 等待异步操作完成
       yield return SomeAsyncOperation();
       
       // 异步操作完成后执行的逻辑
       Debug.Log("异步操作完成");
   }

   IEnumerator SomeAsyncOperation()
   {
       // 异步操作的逻辑
       yield break;
   }

14. 迭代器块和状态机:协程本质上是一种特殊类型的迭代器块。迭代器块是一种可中断和恢复的迭代器,它允许在每次迭代之间保持状态,并在需要时从上次离开的位置继续执行。

   在使用协程时,应该了解协程的执行机制。虽然它们看起来像是同步的代码,但实际上它们是在每帧中部分执行的。这意味着在每个 `yield return` 语句之后,协程将暂停执行并返回到调用它的代码,然后在下一帧继续执行。

PartⅤ

15. Coroutine对象的状态检查:可以使用 `Coroutine` 对象的 `Coroutine.Current` 属性来检查协程的当前状态。状态可以是以下三种之一:

  •    Null:协程未启动或已经完成。
  •    Coroutine:协程正在执行中。
  •    CoroutineSuspended:协程已被暂停。

   这个状态检查可以帮助你在需要时对协程进行额外的控制和处理。

16. 协程与主线程交互:在协程中,可以使用 `yield return null` 来让出主线程,在下一帧继续执行协程。这对于需要与主线程进行交互的场合非常有用,如更新UI元素或处理用户输入。

 IEnumerator MyCoroutine()
   {
       Debug.Log("协程开始执行");
       yield return null; // 让出主线程
       Debug.Log("协程在下一帧继续执行");
   }

   这样,你可以确保在协程执行过程中及时响应主线程的事件。

17. 协程作为事件驱动处理器:协程可以用作事件驱动处理器,以响应事件并执行相关的逻辑。例如,你可以在协程中监听输入事件、定时事件或其他自定义事件,并在相应的事件发生时执行相应的协程代码。

   IEnumerator InputCoroutine()
   {
       while (true)
       {
           if (Input.GetKeyDown(KeyCode.Space))
           {
               Debug.Log("Space键被按下");
               yield return new WaitForSeconds(1f);
           }
           yield return null;
       }
   }

   在这个示例中,协程将持续监听 `Space` 键的按下事件,并在事件发生后等待1秒钟。这种方式可以在不使用传统的事件监听器的情况下,以类似事件驱动的方式响应输入。

这些是关于协程函数和规则的进一步信息。协程是一种非常强大和灵活的功能,可用于处理各种异步、延迟和分步的任务。通过熟悉协程的概念和规则,你可以更好地利用它们来编写高效、可读和易于维护的代码。

PartⅥ

18. 协程组:协程组是一种用于管理多个协程的结构。通过将协程添加到协程组中,你可以对整组协程进行统一的管理操作,如启动、停止和暂停。这对于同时管理多个相关的协程非常有用。 


   IEnumerator CoroutineA()
   {
       yield return new WaitForSeconds(2f);
       Debug.Log("协程A完成");
   }

   IEnumerator CoroutineB()
   {
       yield return new WaitForSeconds(3f);
       Debug.Log("协程B完成");
   }

   void Start()
   {
       CoroutineGroup coroutineGroup = new CoroutineGroup();
       coroutineGroup.AddCoroutine(CoroutineA());
       coroutineGroup.AddCoroutine(CoroutineB());

       StartCoroutine(coroutineGroup.StartGroup());
   }

   在上面的示例中,我们创建了一个协程组 `coroutineGroup`,并向其中添加了协程 `CoroutineA` 和 `CoroutineB`。然后通过 `coroutineGroup.StartGroup()` 方法启动协程组中的所有协程。

19. 协程状态回调:协程状态回调可以让你在协程的各种状态下执行特定的逻辑。通过使用 Unity 的 `IEnumerator` 扩展方法 `OnCoroutineComplete()` 和 `OnCoroutineCanceled()`,你可以定义协程完成或取消时的回调函数。 


   IEnumerator MyCoroutine()
   {
       yield return new WaitForSeconds(2f);
       Debug.Log("协程完成");
   }

   void Start()
   {
       StartCoroutine(MyCoroutine().OnCoroutineComplete(OnCoroutineCompleted));
   }

   void OnCoroutineCompleted()
   {
       Debug.Log("协程完成回调");
   }

   在上面的示例中,我们使用 `OnCoroutineComplete()` 方法将 `OnCoroutineCompleted()` 方法作为回调函数传递给协程。当协程完成时,将调用回调函数。

20. 超时处理:协程可以配合超时处理机制来处理耗时操作。通过在协程中使用计时器和条件判断,你可以控制协程执行的时间,并在超时时执行相应的逻辑。  

  IEnumerator MyCoroutine()
   {
       float startTime = Time.time;
       float timeout = 5f;

       while (Time.time - startTime < timeout)
       {
           // 执行协程逻辑
           yield return null;
       }

       Debug.Log("协程超时");
   }

   在上面的示例中,我们使用计时器和条件判断来控制协程执行的时间。如果协程的执行时间超过设定的超时时间,则输出相应的信息。

PartⅦ

   在上面的示例中,协程A在其执行过程中启动了协程B,并在协程B完成后继续执行自身的逻辑。这种嵌套可以用于处理任务的顺序和依赖关系,使代码更具可读性和可维护性。

21. 协程的动态启动和停止:除了使用 `StartCoroutine()` 和 `StopCoroutine()` 方法外,协程还可以使用 `yield return StartCoroutine()` 和 `yield return StopCoroutine()` 语句来动态地启动和停止协程。


   IEnumerator CoroutineA()
   {
       Debug.Log("协程A开始执行");
       yield return StartCoroutine(CoroutineB()); // 动态启动协程B
       Debug.Log("协程A继续执行");
   }

   IEnumerator CoroutineB()
   {
       Debug.Log("协程B开始执行");
       yield return StartCoroutine(CoroutineC()); // 动态启动协程C
       Debug.Log("协程B完成");
   }

   IEnumerator CoroutineC()
   {
       Debug.Log("协程C开始执行");
       yield return new WaitForSeconds(2f);
       Debug.Log("协程C完成");
   }

   在上面的示例中,协程A启动了协程B,协程B启动了协程C。这种动态启动和停止的方式可以根据需要控制协程的执行流程。

五、举例

using UnityEngine;
using System.Collections;

public class CoroutineExample : MonoBehaviour
{
    private WaitForSeconds waitTime;

    private void Start()
    {
        waitTime = new WaitForSeconds(1f);

        // 启动协程
        StartCoroutine(SpawnObjects());
    }

    private IEnumerator SpawnObjects()
    {
        while (true)
        {
            // 生成一个随机位置的立方体
            Vector3 randomPosition = new Vector3(Random.Range(-5f, 5f), 0f, Random.Range(-5f, 5f));
            GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cube.transform.position = randomPosition;

            // 每隔一秒生成一个新的立方体
            yield return waitTime;
        }
    }
}

在上面的示例中,我们定义了一个 CoroutineExample 的脚本,其中包含了一个协程 SpawnObjects。在 Start() 方法中,我们通过 StartCoroutine() 启动了这个协程。

协程 SpawnObjects 持续生成随机位置的立方体,并在每隔一秒的时间间隔后生成一个新的立方体。通过使用 yield return waitTime 语句,协程将等待一秒钟后继续执行。

这个示例展示了如何利用协程来延迟执行和重复执行一段逻辑,从而实现在游戏中按一定时间间隔生成对象的效果。文章来源地址https://www.toymoban.com/news/detail-685411.html

到了这里,关于Unity——协程(Coroutine)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用团结引擎开发Unity 3D射击游戏

           本案例是初级案例,意在引导想使用unity的初级开发者能较快的入门,体验unity开发的方便性和简易性能。       本次我们将使用团结引擎进行开发,帮助想体验团结引擎的入门开发者进行较快的环境熟悉。      本游戏是一个俯视角度的射击游戏。主角始终位于屏幕

    2024年01月19日
    浏览(72)
  • Unity、UE、Cocos游戏开发引擎的区别

    Unity、Unreal Engine(UE)和Cocos引擎是三个常用的游戏开发引擎,它们在功能和特性上有一些区别。以下是它们之间的主要区别: 编程语言:Unity使用C#作为主要的编程语言,开发者可以使用C#脚本进行游戏逻辑编写。Unreal Engine主要使用C++作为编程语言,但也支持蓝图系统,允许

    2024年02月22日
    浏览(62)
  • Unity vs Godot :哪个游戏引擎更适合你?

    游戏引擎的选择对开发过程和最终产品质量有着重大影响。近年来,Godot和Unity这两款引擎受到广泛关注。本文将从多个维度对两者进行比较,以期为开发者提供正确的选择建议。 Godot和Unity都有各自的优势,没有绝对的好坏之分。Godot开源免费,上手简单,更适合2D和小型游戏

    2024年01月23日
    浏览(93)
  • 30分钟了解所有引擎组件,132个Unity 游戏引擎组件速通!【收藏 == 学会】

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月11日
    浏览(69)
  • Python中协程(coroutine)详解

    1 共用变量问题 多线程中可能出现多个线程争抢变量,所以变量需要加锁;协程中任一时刻都只有一个线程,所以变量不需要加锁。 但是协程虽然不像多线程争抢变量但仍是和多线程一样共用变量的,即共用变量在某处改变在另外一处引用时也会发生改变。 2 协程的适用场景

    2024年04月09日
    浏览(34)
  • Unity Physics2D 2d物理引擎游戏 笔记

    2d 材质 里面可以设置 摩擦力 和 弹力 Simulated:是否在当前的物理环境中模拟,取消勾选该框类似于Disable Rigidbody,但使用这个参数更加高效,因为Disable会销毁内部产生的GameObject,而取消勾选Simulated只是禁用。 Kinematic 动力学刚体 动力学刚体不受重力和力的影响,而受用户的

    2023年04月24日
    浏览(121)
  • Unity和UE4两大游戏引擎,你该如何选择?

    目录 游戏引擎 2 —— 难易区别 编程语言 3 —— 游戏产品 UE4制作的游戏产品  Unity制作的游戏产品  产品类型 5 —— 资源商店 6 —— 人才需求 平均薪资 总结      Unity和UE4都是游戏引擎,所谓游戏引擎就是集成了复杂功能的游戏开发软件,他们帮我们实现了复杂的底层逻

    2023年04月08日
    浏览(69)
  • GODOT游戏引擎简介,包含与unity性能对比测试,以及选型建议

    GODOT,是一个免费开源的3D引擎。本文以unity作对比,简述两者区别和选型建议。由于是很久以前写的ppt,技术原因视频和部分章节丢失了。建议当做业务参考。 GODOT目前为止遇到3个比较重大的机遇,第一个是oprea的合作奖,第二个是用支持c#换来的微软的投资,第三个是虚幻

    2024年02月14日
    浏览(79)
  • Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

    一、简介 2. Unity相当于什么GameObject? 3. 如何设计一个由多种资产、生物等组成的关卡? 4. 在哪里放置特定角色的代码(例如生物、物品)?Unity 中“向 GameObject 添加 MonoBehaviour”相当于什么? 5.Unity子目录相当于什么Assets? 6. 支持哪些模型格式? 7. 支持FBX模型格式吗? 8.

    2024年02月07日
    浏览(76)
  • TBB库中实现协程(coroutine)的源码说明

    源码请见: https://github.com/oneapi-src/oneTBB/blob/master/src/tbb/co_context.h 在windows系统,TBB(也就是intel 的 oneTBB库),通过windwos fiber(纤程)来实现协程(coroutine)。 创建一个协程,代码很简洁: windows系统中线程(thread)与纤程(fiber)调度示意图,如下图所示 windwos中的纤程和通常说的协程类似,

    2024年02月14日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包