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
相关产品推荐
相关产品推荐

