Unity 协程

🌀 Unity 协程(Coroutine)

  • 协程(Coroutine):Unity 提供的一种“分时执行”机制,本质上不是多线程,而是在主线程上分步执行代码,避免卡顿。
  • 适用场景
    1. 异步加载资源或场景
    2. 批量生成对象
    3. 流程控制(动画、等待、定时器等)
    4. 异步操作(网络请求、下载)

1. 协程的本质

  • 协程分为两部分:
    1. 协程函数本体:利用 C# 的迭代器函数(IEnumerator + yield return)实现“分布执行”,可以把函数逻辑拆成多段依次执行。
    2. 协程调度器:Unity 内部实现,用于管理协程函数的执行时机,决定何时继续执行迭代器的下一段。
  • 核心理解:
    • 迭代器函数的 MoveNext() 方法可以遍历函数中各段逻辑,实现分时执行。
    • 协程调度器根据 yield return 返回的内容来决定下一步执行的时机。
  • 理论上,我们可以用迭代器函数自己实现调度器,替代 Unity 的协程调度器。

2. 协程 基本用法

协程函数必须满足两个条件:

  1. 返回值为 IEnumerator 或继承类型
  2. 函数内部使用 yield return 返回值
写法 含义
yield return null 下一帧继续执行
yield return new WaitForSeconds(seconds) 等待指定秒数后执行
yield return new WaitForFixedUpdate() 等待下一帧物理更新
yield return new WaitForEndOfFrame() 等待摄像机和GUI渲染完成后执行
yield break 退出协程
public class Test : MonoBehaviour
{
    void Start()
    {
        // 开启协程函数
        Coroutine c1 = StartCoroutine(MyCoroutine(1, "Hello"));
        Coroutine c2 = StartCoroutine(MyCoroutine(2, "World"));

        // 关闭协程函数
        StopCoroutine(c1);      // 一. 关闭指定协程
        StopAllCoroutines();    // 二. 关闭所有协程
    }

    // 定义协程逻辑
    IEnumerator MyCoroutine(int i, string str)
    {
        print(i);
        yield return null;                       // 下一帧执行
        print(str);
        yield return new WaitForSeconds(1f);     // 等待 1 秒
        print("After 1 second");

        yield return new WaitForFixedUpdate();   // 等待下一帧物理更新
        print("After FixedUpdate");

        yield return new WaitForEndOfFrame();    // 等待渲染完成
        print("After EndOfFrame");

        // 可以循环执行
        while(true)
        {
            print("Looping every 1 second");
            yield return new WaitForSeconds(1f);
        }
    }
}

注意:

  • 协程绑定在 MonoBehaviour 上,如果对象或组件被销毁或失活,协程会自动停止执行。
  • 协程本质是迭代器函数 + 调度器,并不是多线程,因此可以安全访问 Unity API,但不能阻塞主线程。

3. 自定义协程

  • 实现了按自己定的规则来执行逻辑:return的值就是等待多少秒
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Exercise7 : MonoBehaviour
{
    IEnumerator ie;
    IEnumerator MyCoroutine()
    {
        print("1");
        yield return 1;
        print("2");
        yield return 2;
        print("3");
        yield return 3;
        print("4");
        yield return 4;
        print("5");
        yield return 5;
        print("6");
    }
    void Start()
    {
        ie = MyCoroutine();
        CoroutineMgr.Instance.MyStartCoroutine(ie);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

class YieldReturnTime
{
    //记录 下次还要执行的 迭代器接口
    public IEnumerator ie;
    //记录 下次执行的时间点
    public float time;
}

class CoroutineMgr : MonoBehaviour
{
    private static CoroutineMgr instance;
    public static CoroutineMgr Instance => instance;
    //int时间类型协程迭代器
    private List<YieldReturnTime> list = new List<YieldReturnTime>();
    private void Awake()
    {
        instance = this;
    }

    //执行首次协程,加入迭代器列表
    public void MyStartCoroutine(IEnumerator ie)
    {
        if (ie.MoveNext())
        {
            if (ie.Current is int)
            {
                YieldReturnTime y = new YieldReturnTime();
                y.ie = ie;
                y.time = Time.time + (int)ie.Current;
                list.Add(y);
            }
        }
    }

    private void Update()
    {
        for (int i = list.Count - 1; i >= 0; i--)
        {
            //判断 当前该迭代器函数 是否到了下一次要执行的时间
            //如果到了 就需要执行下一步了
            if (list[i].time <= Time.time)
            {
                if (list[i].ie.MoveNext())
                {
                    //如果过是true 那还需要对该迭代器函数进行处理
                    if (list[i].ie.Current is int)
                    {
                        list[i].time = Time.time + (int)list[i].ie.Current;
                    }
                    else
                    {
                        //该list 只是存储 处理时间相关 等待逻辑的 迭代器函数的
                        //如果是别的类型 就不应该 存在这个list中 应该根据类型把它放入别的容器中
                        list.RemoveAt(i);
                    }
                }
                else
                {
                    //后面已经没有可以等待和执行的了 证明已经执行完毕了逻辑
                    list.RemoveAt(i);
                }
            }

        }
    }
}
posted @ 2025-12-10 19:07  高山仰止666  阅读(13)  评论(0)    收藏  举报