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

如何防止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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:31:37