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

在协程中使用Mathf.MoveTowards实现场景过渡的异常问题排查

问题分析与修复方案

核心问题

  1. 协程更新逻辑错误:RoomTransitionHide和RoomTransitionShow的协程没有实现逐帧更新,导致Mathf.MoveTowards仅执行一次就暂停,无法完成完整过渡动画。
  2. 浮点数比较精度问题:直接用==比较材质浮点参数,会因精度误差导致条件永远不满足,协程持续挂起。

修复后的完整代码

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());
    }
}

关键修复点说明

  1. 逐帧更新逻辑:将协程内的yield return new WaitUntil改为yield return null,确保每一帧都执行Mathf.MoveTowards更新参数,实现平滑过渡。
  2. 浮点数精度处理:用Mathf.Approximately替代==比较浮点值,避免因精度误差导致条件判断失效。
  3. 协程等待优化:在RoomManager中直接yield return StartCoroutine(...),确保过渡动画完全完成后再执行房间切换逻辑,流程更严谨。
  4. 目标值缓存:提前计算隐藏过渡的目标值,避免每一帧重复读取材质参数,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 07:09:53