在协程中使用Mathf.MoveTowards实现场景过渡的异常问题排查
问题分析与修复方案
核心问题
- 协程更新逻辑错误:
RoomTransitionHide和RoomTransitionShow的协程没有实现逐帧更新,导致Mathf.MoveTowards仅执行一次就暂停,无法完成完整过渡动画。 - 浮点数比较精度问题:直接用
==比较材质浮点参数,会因精度误差导致条件永远不满足,协程持续挂起。
修复后的完整代码
RoomTransitionController 修正版
public class RoomTransitionController : MonoBehaviour { public static RoomTransitionController Instance { get; private set; } private Image image; public float transitionSpeed = 1.25f; private bool isTransitionComplete; // 缓存目标值,避免重复计算 private float hideTargetValue; private void Awake() { if(Instance != null && Instance != this) { Destroy(this); } else { Instance = this; } } private void Start() { image = GetComponent<Image>(); // 初始化过渡状态 ResetTransitionState(); // 预计算隐藏目标值 hideTargetValue = -0.1f - image.material.GetFloat("_EdgeSmoothing"); } private void ResetTransitionState() { image.material.SetFloat("_Cutoff", 1f); isTransitionComplete = false; } // 改用浮点数近似比较,避免精度问题 public bool TransitionHasLoaded() { return Mathf.Approximately(image.material.GetFloat("_Cutoff"), hideTargetValue); } public IEnumerator RoomTransitionHide() { isTransitionComplete = false; float currentCutoff = image.material.GetFloat("_Cutoff"); // 逐帧更新,直到达到目标值 while (!Mathf.Approximately(currentCutoff, hideTargetValue)) { currentCutoff = Mathf.MoveTowards(currentCutoff, hideTargetValue, transitionSpeed * Time.deltaTime); image.material.SetFloat("_Cutoff", currentCutoff); // 等待下一帧继续更新 yield return null; } isTransitionComplete = true; } public IEnumerator RoomTransitionShow() { isTransitionComplete = false; float currentCutoff = image.material.GetFloat("_Cutoff"); // 逐帧更新,直到回到初始透明状态 while (!Mathf.Approximately(currentCutoff, 1f)) { currentCutoff = Mathf.MoveTowards(currentCutoff, 1f, transitionSpeed * Time.deltaTime); image.material.SetFloat("_Cutoff", currentCutoff); yield return null; } isTransitionComplete = true; } }
RoomManager 优化版
public class RoomManager : MonoBehaviour { public static RoomManager Instance { get; private set; } public Room currentRoom; private void Awake() { if (Instance != null && Instance != this) { Destroy(this); } else { Instance = this; } } public IEnumerator LoadRoom(Room nextRoom) { Debug.Log("开始加载房间: " + nextRoom); // 等待隐藏过渡完成,逻辑更可靠 yield return StartCoroutine(RoomTransitionController.Instance.RoomTransitionHide()); Debug.Log("过渡完成,切换房间"); currentRoom = nextRoom; CameraController.Instance.UpdateCameraPos(currentRoom); // 等待显示过渡完成 yield return StartCoroutine(RoomTransitionController.Instance.RoomTransitionShow()); } }
关键修复点说明
- 逐帧更新逻辑:将协程内的
yield return new WaitUntil改为yield return null,确保每一帧都执行Mathf.MoveTowards更新参数,实现平滑过渡。 - 浮点数精度处理:用
Mathf.Approximately替代==比较浮点值,避免因精度误差导致条件判断失效。 - 协程等待优化:在
RoomManager中直接yield return StartCoroutine(...),确保过渡动画完全完成后再执行房间切换逻辑,流程更严谨。 - 目标值缓存:提前计算隐藏过渡的目标值,避免每一帧重复读取材质参数,提升性能。
内容的提问来源于stack exchange,提问作者CodeConspirator
相关产品推荐
相关产品推荐

