You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何让AnimationCoroutine始终在CameraResizeCoroutine之后执行?

问题描述

我有两个IEnumerator协程函数:

  • AnimationCoroutine():负责逐帧执行程序化动画
  • CameraResizeCoroutine():实现相机尺寸的平滑逐帧调整

由于动画同时涉及游戏对象和UI元素,每帧都需要获取当前相机尺寸来匹配计算,AnimationCoroutine()中有如下代码:

while (elapsed <= duration) {
    cameraSize = Camera.main.orthographicSize;
    tmpScale = cameraSize * SIZE_SCALE_TO_CAMERA * Vector3.one;

    process = Mathf.Clamp01(elapsed / duration);
    // ... 其他动画计算逻辑

    elapsed += Time.deltaTime;
    yield return null;
}

目前的问题是两个协程的执行顺序无法保证,相机调整和动画的执行顺序随机。部分帧中AnimationCoroutine()会先于CameraResizeCoroutine()运行,导致动画计算用上一帧的相机尺寸,出现异常效果。

如何让AnimationCoroutine()每帧都始终在CameraResizeCoroutine()之后执行,达到类似LateUpdate()的效果?

解决方法

方案1:合并协程(最直接)

把相机调整和动画逻辑放到同一个协程里,强制顺序执行——每帧先处理相机尺寸调整,再执行动画计算,从根源上避免顺序问题:

IEnumerator CombinedCoroutine(float animationDuration, float resizeDuration)
{
    float animElapsed = 0f;
    float resizeElapsed = 0f;

    while (animElapsed <= animationDuration || resizeElapsed <= resizeDuration)
    {
        // 优先执行相机调整逻辑
        if (resizeElapsed <= resizeDuration)
        {
            float resizeProcess = Mathf.Clamp01(resizeElapsed / resizeDuration);
            Camera.main.orthographicSize = Mathf.Lerp(startSize, targetSize, resizeProcess);
            resizeElapsed += Time.deltaTime;
        }

        // 再执行动画计算逻辑
        if (animElapsed <= animationDuration)
        {
            float cameraSize = Camera.main.orthographicSize;
            Vector3 tmpScale = cameraSize * SIZE_SCALE_TO_CAMERA * Vector3.one;
            float process = Mathf.Clamp01(animElapsed / animationDuration);
            // ... 其他动画逻辑

            animElapsed += Time.deltaTime;
        }

        yield return null;
    }
}

方案2:用自定义标记控制执行时机

通过静态标记位记录相机调整是否完成当前帧的逻辑,让动画协程等待标记位触发后再执行。

首先定义一个自定义Yield指令和辅助类:

public class WaitForCameraResize : CustomYieldInstruction
{
    public override bool keepWaiting => !CameraResizeHelper.IsResizeFrameCompleted;
}

// 用辅助类存标记位,避免耦合到具体脚本
public static class CameraResizeHelper
{
    public static bool IsResizeFrameCompleted = false;
}

然后修改CameraResizeCoroutine(),每帧更新标记位状态:

IEnumerator CameraResizeCoroutine()
{
    while (/* 相机调整未完成的判断条件 */)
    {
        CameraResizeHelper.IsResizeFrameCompleted = false;

        // 执行相机尺寸平滑调整的逻辑
        // ...

        CameraResizeHelper.IsResizeFrameCompleted = true;
        yield return null;
    }
    // 协程结束后确保标记位为true,避免动画协程无限等待
    CameraResizeHelper.IsResizeFrameCompleted = true;
}

最后修改AnimationCoroutine(),每帧先等待相机调整完成:

IEnumerator AnimationCoroutine()
{
    while (elapsed <= duration)
    {
        // 等待当前帧相机调整完成
        yield return new WaitForCameraResize();

        cameraSize = Camera.main.orthographicSize;
        tmpScale = cameraSize * SIZE_SCALE_TO_CAMERA * Vector3.one;

        process = Mathf.Clamp01(elapsed / duration);
        // ... 其他动画逻辑

        elapsed += Time.deltaTime;
        yield return null;
    }
}

方案3:利用Unity内置的帧阶段控制

Unity的协程yield return null对应Update()阶段,而yield return new WaitForLateUpdate()对应LateUpdate()阶段。如果CameraResizeCoroutine()用默认的yield return null(在Update()执行),那么让AnimationCoroutine()每帧等待到LateUpdate()阶段再执行,就能保证顺序:

修改AnimationCoroutine()的yield语句:

IEnumerator AnimationCoroutine()
{
    while (elapsed <= duration)
    {
        cameraSize = Camera.main.orthographicSize;
        tmpScale = cameraSize * SIZE_SCALE_TO_CAMERA * Vector3.one;

        process = Mathf.Clamp01(elapsed / duration);
        // ... 其他动画逻辑

        elapsed += Time.deltaTime;
        // 等待到LateUpdate阶段再进入下一循环,确保相机调整已经完成
        yield return new WaitForLateUpdate();
    }
}

注意:如果CameraResizeCoroutine()本身是在LateUpdate()执行的,那这个方案不适用,需要调整为让相机调整在更早的阶段执行。


内容的提问来源于stack exchange,提问作者Freshman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 15:39:53