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

Unity中Transform插值动画进度远超预期的问题求助

保险丝动画时长异常问题修复

问题场景

我需要让保险丝(fuse)对象平滑飞向保险丝盒(fuse box),通过Vector3/Quaternion.Lerp逐步适配位置和旋转,预期动画时长100秒,但实际几秒就完成了,无法看到正常过渡效果。

声明的变量

private Transform closestFuseBoxTransform = null;
private Transform initialFuseTransform = null;
private float fuseSnappingProgress = -1f;
private float fuseSnapingAnimationTime = 100f;
private float fuseSnappingBezierStrength = 1f;

当前Update方法代码

if (Input.GetKeyDown(KeyCode.Space))
{
    // 获取初始和目标Transform
    closestFuseBoxTransform = GetClosestObject(GameObject.FindGameObjectsWithTag("FuseDst"));
    initialFuseTransform = transform;

    // 启用动画逻辑
    fuseSnappingProgress = 0f;
    Debug.Log(transform.rotation.eulerAngles);
}

if (fuseSnappingProgress >= 0f)
{
    // 递增实时计时器
    fuseSnappingProgress += Time.deltaTime / fuseSnapingAnimationTime;

    // 实现平滑过渡,利用x^y(y>0)在0<x<1时返回0<y<1的特性
    float fuseSnappingProgressBezier = Mathf.Pow(fuseSnappingProgress, fuseSnappingBezierStrength);
    Debug.Log(fuseSnappingProgressBezier);

    // 使用初始位置、目标位置和计算出的fuseSnappingProgressBezier设置Transform值
    transform.position = Vector3.Lerp(initialFuseTransform.position, closestFuseBoxTransform.position,
                                      fuseSnappingProgressBezier);
    transform.rotation = Quaternion.Lerp(initialFuseTransform.rotation, closestFuseBoxTransform.rotation,
                                      fuseSnappingProgressBezier);

    // 当动画完成100%时
    if (fuseSnappingProgress >= 1f)
    {
        // 修正可能因大Time.deltaTime导致的进度跳变问题
        transform.position = closestFuseBoxTransform.position;
        transform.rotation = closestFuseBoxTransform.rotation;

        // 关闭动画逻辑
        fuseSnappingProgress = -1f;
        Debug.Log("Fuse snapped into closest Fuse Box");
    }
}

问题原因

核心问题是你把initialFuseTransform直接赋值为transform,这意味着它始终指向当前保险丝的Transform组件,而非动画启动时的初始状态。随着动画执行,initialFuseTransform.position会跟着保险丝的移动不断更新,导致Vector3.Lerp的起始点越来越接近目标点,动画自然会快速结束。

另外还有个拼写小错误:fuseSnapingAnimationTime少了一个字母p,虽不影响运行,但会增加维护混淆的风险。

解决方案

不要保存Transform引用,而是用单独变量存储动画启动时的初始位置和初始旋转值:

修改后的变量声明

private Transform closestFuseBoxTransform = null;
private Vector3 initialFusePosition; // 存储动画启动时的初始位置
private Quaternion initialFuseRotation; // 存储动画启动时的初始旋转
private float fuseSnappingProgress = -1f;
private float fuseSnappingAnimationTime = 100f; // 修正拼写错误,统一命名规范
private float fuseSnappingBezierStrength = 1f;

修改后的Update方法代码

if (Input.GetKeyDown(KeyCode.Space))
{
    // 获取目标Transform
    closestFuseBoxTransform = GetClosestObject(GameObject.FindGameObjectsWithTag("FuseDst"));
    // 保存动画启动瞬间的初始位置和旋转
    initialFusePosition = transform.position;
    initialFuseRotation = transform.rotation;

    // 启用动画逻辑
    fuseSnappingProgress = 0f;
    Debug.Log(transform.rotation.eulerAngles);
}

if (fuseSnappingProgress >= 0f)
{
    // 递增实时计时器
    fuseSnappingProgress += Time.deltaTime / fuseSnappingAnimationTime;
    // 限制进度最大值为1,避免帧率波动导致进度超标
    fuseSnappingProgress = Mathf.Min(fuseSnappingProgress, 1f);

    // 实现平滑过渡
    float fuseSnappingProgressBezier = Mathf.Pow(fuseSnappingProgress, fuseSnappingBezierStrength);

    // 使用保存的初始值进行插值计算
    transform.position = Vector3.Lerp(initialFusePosition, closestFuseBoxTransform.position,
                                      fuseSnappingProgressBezier);
    transform.rotation = Quaternion.Lerp(initialFuseRotation, closestFuseBoxTransform.rotation,
                                      fuseSnappingProgressBezier);

    // 当动画完成时
    if (fuseSnappingProgress >= 1f)
    {
        // 修正最终位置和旋转,确保完全对齐
        transform.position = closestFuseBoxTransform.position;
        transform.rotation = closestFuseBoxTransform.rotation;

        // 关闭动画逻辑
        fuseSnappingProgress = -1f;
        Debug.Log("保险丝已卡入最近的保险丝盒");
    }
}

额外优化点

  1. 修正变量名拼写错误,统一代码命名规范
  2. 添加Mathf.Min限制进度最大值,避免异常情况
  3. 将日志改为中文,更贴合本地化需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:30:42