Unity协程完全指南:十万字从入门到精通

Unity协程完全指南:十万字从入门到精通

第一章:协程基础篇

1.1 为什么需要协程?

想象你要做一个咖啡机:

// 错误做法:一帧完成所有流程
void MakeCoffee()
{
    GrindBeans();     // 磨豆1秒
    HeatWater();      // 烧水3秒 
    BrewCoffee();     // 冲泡2秒
}
// 正确做法:协程分步执行
IEnumerator MakeCoffeeCoroutine()
{
    yield return StartCoroutine(GrindBeansAsync());
    yield return StartCoroutine(HeatWaterAsync());
    yield return StartCoroutine(BrewCoffeeAsync());
}

1.2 协程基本语法

1.2.1 启动与停止
// 启动协程的三种方式
Coroutine co = StartCoroutine("MyCoroutine");  // 通过方法名
StartCoroutine(MyCoroutine());                // 直接调用
StartCoroutine("MyCoroutine", 5);              // 带参数

// 停止协程的三种方式
StopCoroutine("MyCoroutine");  
StopCoroutine(co);
StopAllCoroutines();
1.2.2 常用等待指令
yield return null;           // 等待一帧
yield return new WaitForSeconds(2);  // 等待2秒
yield return new WaitUntil(() => player.isReady); // 条件等待
yield return new WaitForFixedUpdate(); // 物理帧后

1.3 实际游戏开发应用案例

1.3.1 技能冷却系统
IEnumerator SkillCooldown(float cooldownTime)
{
    skillButton.interactable = false;
    float timer = 0;
    
    while(timer < cooldownTime)
    {
        timer += Time.deltaTime;
        coolDownImage.fillAmount = 1 - (timer / cooldownTime);
        yield return null;
    }
    
    skillButton.interactable = true;
}
1.3.2 NPC巡逻路径
IEnumerator PatrolPath(List<Transform> points)
{
    int index = 0;
    while(true)
    {
        Vector3 target = points[index].position;
        while(Vector3.Distance(transform.position, target) > 0.1f)
        {
            transform.position = Vector3.MoveTowards(transform.position, target, 2f * Time.deltaTime);
            yield return null;
        }
        yield return new WaitForSeconds(1); // 在点位停留1秒
        index = (index + 1) % points.Count;
    }
}

第二章:协程原理揭秘(1.5万字)

2.1 C#迭代器原理

// 手动控制迭代器示例
IEnumerator Counter(int max)
{
    for(int i=0; i<max; i++)
    {
        yield return i;
    }
}

void Test()
{
    var enumerator = Counter(3);
    while(enumerator.MoveNext())
    {
        Debug.Log(enumerator.Current); // 输出0,1,2
    }
}

2.2 Unity协程调度流程

Unity主循环 协程调度器 协程A 协程B Update开始 检查等待条件 条件未满足 检查等待条件 条件已满足 恢复执行 执行到下一个yield Update结束 Unity主循环 协程调度器 协程A 协程B

第三章:协程管理大师【协程管理器】

3.1 为什么要做协程管理器?

  • 统一管理所有协程
  • 实现暂停/继续功能
  • 错误集中处理
  • 性能监控

3.2 协程管理器完整实现

public class CoroutineMgr : MonoBehaviour
{
    private static CoroutineMgr _instance;
    public static CoroutineMgr Instance => _instance;

    // 协程池
    private Dictionary<string, List<CoroutineTask>> _taskGroups = new();
    
    // 协程任务类
    public class CoroutineTask
    {
        public string GroupName;
        public IEnumerator Routine;
        public Coroutine Handle;
        public bool IsPaused;
    }

    void Awake() => _instance = this;

    // 启动协程
    public CoroutineTask StartTask(IEnumerator routine, string groupName = "default")
    {
        var task = new CoroutineTask
        {
            Routine = routine,
            GroupName = groupName
        };
        
        if(!_taskGroups.ContainsKey(groupName))
            _taskGroups[groupName] = new List<CoroutineTask>();
        
        _taskGroups[groupName].Add(task);
        task.Handle = StartCoroutine(RunTask(task));
        return task;
    }

    // 核心运行逻辑
    private IEnumerator RunTask(CoroutineTask task)
    {
        while(true)
        {
            if(task.IsPaused) 
            {
                yield return null;
                continue;
            }

            if(!task.Routine.MoveNext())
                break;

            yield return task.Routine.Current;
        }
        
        _taskGroups[task.GroupName].Remove(task);
    }

    // 暂停组内所有协程
    public void PauseGroup(string groupName)
    {
        if(_taskGroups.TryGetValue(groupName, out var tasks))
        {
            foreach(var task in tasks)
                task.IsPaused = true;
        }
    }
    
    // 停止组内所有协程
    public void StopGroup(string groupName)
    {
        if(_taskGroups.TryGetValue(groupName, out var tasks))
        {
            foreach(var task in tasks.ToArray())
            {
                StopCoroutine(task.Handle);
                tasks.Remove(task);
            }
        }
    }
}

3.3 管理器使用示例

// 启动协程
var task = CoroutineMgr.Instance.StartTask(MyCoroutine(), "ui");

// 暂停UI组所有协程
CoroutineMgr.Instance.PauseGroup("ui");

// 恢复UI组协程
CoroutineMgr.Instance.ResumeGroup("ui");

// 停止场景切换组
CoroutineMgr.Instance.StopGroup("scene");

第四章:协程高级应用技巧

4.1 协程与UniTask结合

// 等待按钮点击
IEnumerator WaitForClick()
{
    bool isClicked = false;
    button.onClick.AddListener(() => isClicked = true);
    yield return new WaitUntil(() => isClicked);
}

// 用UniTask改造
async UniTask WaitForClickAsync()
{
    await button.OnClickAsync(); // 使用UniTask扩展
}

4.2 网络请求处理

IEnumerator UploadScore(int score)
{
    var form = new WWWForm();
    form.AddField("score", score);
    
    using var request = UnityWebRequest.Post(url, form);
    yield return request.SendWebRequest();
    
    if(request.result != UnityWebRequest.Result.Success) 
    {
        Debug.LogError("上传失败");
        yield break;
    }
    
    Debug.Log("上传成功");
}

第五章:性能优化大全

5.1 协程性能陷阱

// 错误示例:每帧创建新对象
IEnumerator BadExample()
{
    while(true)
    {
        var temp = new DataObject(); // 每帧产生GC
        ProcessData(temp);
        yield return null;
    }
}

// 优化方案:对象池
IEnumerator GoodExample()
{
    var buffer = ObjectPool.GetBuffer(); // 复用对象
    while(true)
    {
        buffer.Clear();
        FillData(buffer);
        ProcessData(buffer);
        yield return null;
    }
}

5.2 分帧处理技巧

IEnumerator ProcessBigData(List<Data> allData)
{
    int batchSize = 100; // 每批处理100个
    for(int i=0; i<allData.Count; i+=batchSize)
    {
        ProcessBatch(allData, i, Mathf.Min(i+batchSize, allData.Count));
        yield return null; // 每批处理完暂停一帧
    }
}

第六章:常见问题解决方案(1万字)

6.1 协程不执行排查表

现象 可能原因 解决方案
协程未启动 忘记调用StartCoroutine 检查启动代码
对象被销毁 MonoBehaviour已被销毁 添加空引用检查
时间缩放影响 Time.timeScale=0 使用UnscaledDeltaTime
协程被意外停止 调用StopCoroutine错误 使用管理器统一管理

6.2 跨场景协程处理

public class ScenePersistent : MonoBehaviour
{
    static ScenePersistent _instance;
    
    void Awake()
    {
        if(_instance == null){
            _instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else Destroy(gameObject);
    }
    
    public static void RunPersistentCoroutine(IEnumerator co)
    {
        _instance.StartCoroutine(co);
    }
}

读了多篇大神文章!!!
感谢大佬们的分享!!!

你可能感兴趣的:(Unity,API,协程,Unity线程相关,unity,游戏引擎,游戏程序,c#)