如何防止Unity中Coroutine未完成时重复触发导致Slerp重启?
解决Unity中Slerp旋转协程重复触发的问题
嘿,这个问题在Unity开发里挺常见的,你一开始想到用布尔值判断的思路其实完全可行,不过还有几种更灵活的方案,我给你梳理一下:
方法一:布尔标记锁(你最初的思路)
这个方案简单直接,用一个布尔变量标记当前是否有旋转协程在运行。每次用户按下按钮时先检查这个标记,如果正在旋转就直接跳过,否则再启动协程,协程结束后再把标记重置。
适合需要让用户等待当前旋转完成才能触发下一次的场景,比如一些需要精准操作的关卡。
代码示例:
private bool isRotating = false; public Transform targetObject; public float rotateDuration = 1f; // 按钮点击触发的方法 public void OnRotateButtonClick(Vector3 targetEulerAngles) { // 正在旋转就直接返回,不触发新的协程 if (isRotating) return; StartCoroutine(RotateToTargetAngle(targetEulerAngles)); } private IEnumerator RotateToTargetAngle(Vector3 targetAngles) { isRotating = true; Quaternion startRotation = targetObject.rotation; Quaternion targetRotation = Quaternion.Euler(targetAngles); float elapsedTime = 0f; while (elapsedTime < rotateDuration) { elapsedTime += Time.deltaTime; // 可以用SmoothStep做缓动,让旋转更自然 float progress = Mathf.SmoothStep(0f, 1f, elapsedTime / rotateDuration); targetObject.rotation = Quaternion.Slerp(startRotation, targetRotation, progress); yield return null; } // 确保最后旋转到精确的目标角度 targetObject.rotation = targetRotation; isRotating = false; }
方法二:中断旧协程,启动新的(更流畅的交互)
如果希望用户按下新按钮时,立刻放弃当前旋转转向新目标,不需要等待旧动画完成,那可以在每次触发时先停止正在运行的协程,再启动新的。这种方式用户体验更流畅,适合快速响应的交互场景。
代码示例:
private Coroutine currentRotateCoroutine; public Transform targetObject; public float rotateDuration = 1f; public void OnRotateButtonClick(Vector3 targetEulerAngles) { // 如果有正在运行的旋转协程,先停止它 if (currentRotateCoroutine != null) { StopCoroutine(currentRotateCoroutine); } // 启动新的旋转协程并保存引用 currentRotateCoroutine = StartCoroutine(RotateToTargetAngle(targetEulerAngles)); } private IEnumerator RotateToTargetAngle(Vector3 targetAngles) { Quaternion startRotation = targetObject.rotation; Quaternion targetRotation = Quaternion.Euler(targetAngles); float elapsedTime = 0f; while (elapsedTime < rotateDuration) { elapsedTime += Time.deltaTime; float progress = elapsedTime / rotateDuration; targetObject.rotation = Quaternion.Slerp(startRotation, targetRotation, progress); yield return null; } targetObject.rotation = targetRotation; // 协程结束后清空引用 currentRotateCoroutine = null; }
方法三:用补间插件简化逻辑(懒人福音)
如果不想自己写协程的生命周期管理,直接用Unity生态里的补间动画插件就行,比如DOTween或者LeanTween,这些插件已经帮你处理了重复触发的问题,代码会异常简洁。
比如用DOTween的示例:
using DG.Tweening; public Transform targetObject; public float rotateDuration = 1f; public void OnRotateButtonClick(Vector3 targetEulerAngles) { // 直接调用DORotate,重复调用时会自动停止旧动画并启动新的 targetObject.DORotate(targetEulerAngles, rotateDuration, RotateMode.Fast) .SetEase(Ease.InOutQuad); // 可选:加个缓动曲线让动画更丝滑 }
总结一下
- 要强制等待当前旋转完成:选方法一的布尔标记;
- 要即时响应用户新操作:选方法二的协程中断,或者方法三的补间插件;
内容的提问来源于stack exchange,提问作者Dtb49
相关产品推荐
相关产品推荐

