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

Unity2D中Vector3.Lerp实现移动管道开合动画失效求助

问题分析与解决:Unity2D管道移动+开合动画异常

问题描述

在Flappy Bird类2D游戏中,尝试实现管道(Top和Bottom两个子物体)在向左移动的同时平滑开合的效果,通过W键触发。但实际运行时,管道会直接瞬移到开合后的位置,动画期间管道停止向左移动,完全不符合预期。

原因分析

  1. Lerp起点每帧重置:TopStartPoint和BottomStartPoint在Update中每帧重新计算,导致Vector3.Lerp的起始位置不断变化,无法完成从初始位置到目标位置的平滑插值,最终表现为瞬移。
  2. 父物体移动与子物体位置修改冲突:父物体(管道整体)通过transform.position向左移动,同时子物体(Top/Bottom)的世界位置被Lerp直接覆盖,两者的位置更新逻辑互相干扰,导致动画期间管道停止移动。
  3. 目标位置计算冗余:原本试图计算X轴偏移,但实际上管道的X轴移动由父物体统一控制,额外计算反而导致位置逻辑混乱。

修正后的代码

using UnityEngine;

public class PipeMove : MonoBehaviour
{
    public float deadZone = -15;
    public float MoveSpeed;
    public BorbPhysics borb;
    public float DeathMoveSpeed = 1;
    public GameObject Top;
    public GameObject Bottom;
    public float Offset = 5;
    // 改为存储局部位置的起始/目标点
    private Vector3 TopStartLocalPos;
    private Vector3 BottomStartLocalPos;
    private Vector3 TopEndLocalPos;
    private Vector3 BottomEndLocalPos;
    public float desiredDuration = 3f;
    private float elapsedTime;
    public float TyStart;
    public float ByStart;
    public bool IsOpening = false;
    public bool hasSet = false;

    void Start()
    {
        borb = GameObject.FindGameObjectWithTag("borb").GetComponent<BorbPhysics>();
        // 记录初始局部Y位置
        TyStart = Top.transform.localPosition.y;
        ByStart = Bottom.transform.localPosition.y;
    }

    void Update()
    {
        // 管道整体移动逻辑,保持不变
        if (borb.BorbIsAlive)
        {
            transform.position += Vector3.left * MoveSpeed * Time.deltaTime;
        }
        else
        {
            transform.position += Vector3.left * DeathMoveSpeed * Time.deltaTime;
        }

        if (transform.position.x < deadZone)
        {
            Destroy(gameObject);
        }

        // 触发开合逻辑
        if (Input.GetKeyDown(KeyCode.W) && !IsOpening)
        {
            IsOpening = true;
            hasSet = false;
            elapsedTime = 0f;
        }

        if (IsOpening)
        {
            // 仅在触发时初始化一次起始和目标位置
            if (!hasSet)
            {
                // 记录触发时的局部位置作为起点
                TopStartLocalPos = Top.transform.localPosition;
                BottomStartLocalPos = Bottom.transform.localPosition;
                // 计算目标局部位置(只修改Y轴,X轴跟随父物体移动)
                TopEndLocalPos = new Vector3(TopStartLocalPos.x, TyStart + Offset, 0);
                BottomEndLocalPos = new Vector3(BottomStartLocalPos.x, ByStart - Offset, 0);
                hasSet = true;
            }

            elapsedTime += Time.deltaTime;
            float percentageComplete = Mathf.Clamp01(elapsedTime / desiredDuration);

            // 修改局部位置,父物体的移动会自动带动子物体X轴移动
            Top.transform.localPosition = Vector3.Lerp(TopStartLocalPos, TopEndLocalPos, percentageComplete);
            Bottom.transform.localPosition = Vector3.Lerp(BottomStartLocalPos, BottomEndLocalPos, percentageComplete);

            if (percentageComplete >= 1f)
            {
                IsOpening = false;
                hasSet = false;
            }
        }
    }
}

关键改动说明

  • 固定Lerp起始位置:仅在触发开合时初始化TopStartLocalPos和BottomStartLocalPos,避免每帧重置导致插值失效。
  • 使用局部位置(localPosition):子物体的开合动画修改局部位置,父物体负责整体向左移动,两者逻辑分离,确保动画期间管道持续移动。
  • 简化目标位置计算:开合仅修改Y轴位置,X轴跟随父物体自然移动,无需额外计算X坐标的偏移,逻辑更清晰。
  • 优化状态控制:添加Mathf.Clamp01确保插值进度不会超过1,同时调整触发逻辑避免重复初始化。

内容的提问来源于stack exchange,提问作者Noé Crespo García

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:12:05