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

Unity 3D枪械行走时平滑Lerp上下移动问题求助

修复FPS行走时枪械生硬抖动的问题

现有代码的问题分析

  • Invoke与FixedUpdate时序不匹配:FixedUpdate是固定时间间隔执行,Invoke按实际时间延迟触发,两者节奏冲突会导致枪械移动触发时机混乱,产生抖动。
  • 单次Lerp缺乏持续插值:当前仅在触发方向键的帧执行一次Lerp,后续无持续插值到目标位置,导致移动生硬不连贯。
  • 状态控制逻辑简陋:仅用canGoUp布尔值切换上下状态,持续按住方向键时会反复触发切换,引发抖动。
  • Transform与Rigidbody冲突:同时操作枪械的Transform和Rigidbody,直接修改位置会干扰物理引擎计算,导致抖动。

修复方案:基于移动速度的平滑晃动

以下是修改后的代码,核心思路是用角色移动速度驱动枪械晃动,通过正弦函数模拟自然脚步节奏,同时持续执行Lerp保证平滑过渡:

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    [Header("References")]
    [SerializeField] Transform orientation;
    [SerializeField] Animator walkAnim;
    [SerializeField] Transform gun; // 移除gunRB,避免物理与Transform冲突
    Rigidbody rb;

    [Header("Movement Settings")]
    [SerializeField] float speed = 2000f;

    [Header("Gun Sway Settings")]
    [SerializeField] float walkSwayAmount = 0.1f; // 晃动幅度
    [SerializeField] float walkSwaySpeed = 8f; // 晃动频率,匹配脚步节奏
    [SerializeField] float swaySmoothness = 5f; // 平滑过渡速度

    private Vector3 initialGunPos;
    private float swayTimer;

    void Start()
    {
        initialGunPos = gun.localPosition;
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        // 玩家移动逻辑保留在FixedUpdate(物理相关)
        Vector3 moveDir = Vector3.zero;
        if (Input.GetKey(KeyCode.W)) moveDir += orientation.forward;
        if (Input.GetKey(KeyCode.S)) moveDir -= orientation.forward;
        if (Input.GetKey(KeyCode.A)) moveDir -= orientation.right;
        if (Input.GetKey(KeyCode.D)) moveDir += orientation.right;

        // 标准化方向,避免斜向移动速度过快
        if (moveDir.magnitude > 1f) moveDir.Normalize();
        rb.AddForce(moveDir * speed * Time.deltaTime, ForceMode.Acceleration);
    }

    void Update()
    {
        // 获取角色平面移动速度(忽略Y轴)
        Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        float moveSpeed = flatVelocity.magnitude;

        // 移动时触发晃动
        if (moveSpeed > 0.1f)
        {
            swayTimer += Time.deltaTime * walkSwaySpeed;
            // 正弦函数模拟自然上下晃动
            float yOffset = Mathf.Sin(swayTimer) * walkSwayAmount;
            Vector3 targetPos = initialGunPos + new Vector3(0f, yOffset, 0f);
            
            // 每帧执行Lerp,保证平滑过渡
            gun.localPosition = Vector3.Lerp(gun.localPosition, targetPos, swaySmoothness * Time.deltaTime);
        }
        else
        {
            // 停止移动时平滑重置到初始位置
            gun.localPosition = Vector3.Lerp(gun.localPosition, initialGunPos, swaySmoothness * Time.deltaTime);
            swayTimer = 0f;
        }

        // 更新行走动画(可选,根据移动速度触发)
        walkAnim.SetFloat("MoveSpeed", moveSpeed);
    }
}

关键改进点

  • 移动速度驱动晃动:通过角色实际移动速度判断是否触发晃动,比检测按键更准确(比如角色被阻挡时不会误触发)。
  • 正弦曲线模拟自然节奏:正弦曲线的平滑特性能还原真实行走时的枪械晃动,避免生硬切换。
  • 持续Lerp插值:在Update中每帧执行Lerp,保证枪械位置平滑过渡,无跳变。
  • 逻辑分离:移动逻辑留在FixedUpdate(物理相关),枪械晃动移到Update(动画渲染相关),时序更匹配。

替代方案:回到动画系统解决原脚本失效问题

如果之前的武器晃动脚本因添加行走/idle动画失效,大概率是动画层权重冲突或动画覆盖了Transform属性,可尝试:

  • 给武器晃动动画单独创建动画层,设置更高权重,确保不会被行走/idle动画覆盖。
  • 在Animator中使用动画曲线,将移动速度作为参数传入,驱动曲线控制枪械位置偏移,实现自然联动。
  • 用反向运动学(IK) 控制枪械位置,避免动画直接修改Transform,保留脚本控制权限。

内容的提问来源于stack exchange,提问作者Bred Bredboi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 04:15:41