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; } } }
问题分析与解决方案
核心问题
- 多协程冲突:重复点击会启动多个
OpenDoor协程,多个协程同时修改门的旋转,导致运动混乱、速度异常。 - Lerp起始值错误:每次调用
Vector3.Slerp时用当前旋转作为起始值,加上多协程干扰,会让插值进度完全偏离预期。 - 无动画锁机制:没有判断动画是否正在执行,导致重复触发。
修改后的代码
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
相关产品推荐
相关产品推荐

