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

Unity协程实现门平滑开关异常问题求助

门平滑旋转协程问题解决

问题描述

我不清楚如何在协程中管理旋转,所有交互物体都通过通用交互脚本处理,所以用协程实现门的旋转。但当前脚本达不到预期效果:我想要门实现简单的平滑开关运动,但脚本只能完成一次缓慢运动,之后得等重置才能再次缓慢运动;重复点击交互按钮时,门会快速开关。希望能得到帮助实现门的平滑运动。

原代码

using System.Collections;
using UnityEngine;

public class DoorsWithoutKey : MonoBehaviour
{ 
    public bool open = false;
   
    [SerializeField] private float smooth = 2.0f;
    [SerializeField] private float DoorOpenAngle = 90.0f;
    private Vector3 defaulRot;
    private Vector3 openRot;
    [SerializeField] public float lerpDuration = 0;

    void Start()
    {
        defaulRot = transform.eulerAngles;
        openRot = new Vector3(defaulRot.x, defaulRot.y + DoorOpenAngle, defaulRot.z);
    }


    public void NewOpenClosed()
    {
        open = !open;
        StartCoroutine(OpenDoor());
    }

    private IEnumerator OpenDoor()
    {
        float timeElapsed = 0;

        while (timeElapsed < lerpDuration)
        {

            if (open)
            {
                gameObject.transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, openRot, timeElapsed / lerpDuration);
            }
            else
            {
                gameObject.transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, defaulRot, timeElapsed / lerpDuration);
            }
            timeElapsed += Time.deltaTime;
            yield return null;

        }        
    }
}

问题分析与解决方案

核心问题

  1. 多协程冲突:重复点击会启动多个OpenDoor协程,多个协程同时修改门的旋转,导致运动混乱、速度异常。
  2. Lerp起始值错误:每次调用Vector3.Slerp时用当前旋转作为起始值,加上多协程干扰,会让插值进度完全偏离预期。
  3. 无动画锁机制:没有判断动画是否正在执行,导致重复触发。

修改后的代码

using System.Collections;
using UnityEngine;

public class DoorsWithoutKey : MonoBehaviour
{ 
    public bool open = false;
    // 标记是否正在执行动画,避免重复触发
    private bool isAnimating = false;
   
    [SerializeField] private float DoorOpenAngle = 90.0f;
    private Vector3 defaultRot;
    private Vector3 openRot;
    [SerializeField] private float lerpDuration = 1.0f; // 建议设置大于0的值,比如1秒

    void Start()
    {
        defaultRot = transform.eulerAngles;
        openRot = new Vector3(defaultRot.x, defaultRot.y + DoorOpenAngle, defaultRot.z);
    }


    public void NewOpenClosed()
    {
        if (isAnimating) return;
        
        open = !open;
        StartCoroutine(OpenDoorCoroutine());
    }

    private IEnumerator OpenDoorCoroutine()
    {
        isAnimating = true;
        float timeElapsed = 0;
        // 记录动画开始时的当前旋转,作为插值起始点
        Vector3 startRot = transform.eulerAngles;
        // 确定本次动画的目标旋转
        Vector3 targetRot = open ? openRot : defaultRot;

        while (timeElapsed < lerpDuration)
        {
            // 使用固定起始点和目标点插值,保证动画平滑可控
            transform.eulerAngles = Vector3.Slerp(startRot, targetRot, timeElapsed / lerpDuration);
            timeElapsed += Time.deltaTime;
            yield return null;
        }
        // 动画结束后强制设置到目标值,消除浮点计算误差
        transform.eulerAngles = targetRot;
        isAnimating = false;
    }
}

关键改动说明

  • 动画锁isAnimating:点击时先判断是否正在执行动画,防止重复启动协程,解决快速点击导致的混乱。
  • 固定起始旋转startRot:动画开始时记录当前旋转,整个插值过程用这个固定起始点,避免中途被其他操作干扰。
  • 明确目标旋转targetRot:动画开始时就确定本次要到达的目标,不用在循环里反复判断open状态,逻辑更清晰。
  • 结束位置修正:动画循环结束后强制设置到目标值,消除浮点计算带来的微小误差。
  • 规范变量名:把defaulRot改成defaultRot,符合代码命名规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:43:14