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

