FPS游戏中如何抵消后坐力且不改变视角基础俯仰角
FPS游戏中如何抵消后坐力且不改变视角基础俯仰角
嘿,我完全懂你遇到的这个坑!之前做FPS项目时也踩过一模一样的雷——现在的逻辑里,玩家为了抵消后坐力的上抬而下拉鼠标的操作,会被直接计入基础俯仰角_cameraTargetPitch里,等后坐力回弹归零后,基础视角就停在被拉低的位置了对吧?
问题根源拆解
当前你的后坐力是通过_currentRecoil乘在最终旋转上实现的,而玩家的鼠标输入(包括用来抵消后坐力的下拉操作)全部累加给了_cameraTargetPitch。这就导致:
玩家下拉鼠标 → 基础俯仰角被拉低 → 后坐力消失后,视角停在低角度
核心解决思路就是把「玩家用来抵消后坐力的操作」和「调整基础视角的操作」彻底分开,让抵消后坐力的鼠标输入不会影响基础俯仰角。下面给你两种适配你现有代码的修改方案,选顺手的用就行:
方案一:在输入阶段直接分离抵消操作(贴近你现有代码结构)
这个方案不需要大改现有逻辑,只需要在处理鼠标输入时,把用来抵消后坐力的部分单独抽出来,不让它影响基础俯仰角:
修改后的完整代码
// 新增两个可调参数,用来优化手感 [SerializeField] private float _recoilCounteractMultiplier = 1.5f; // 抵消后坐力的灵敏度 [SerializeField] private float _recoilNeutralizeSpeed = 20f; // 输入抵消后坐力的速度 private void Update() { _isAiming = _aimAction.IsPressed(); _isSprinting = _sprintAction.IsPressed(); bool isFiring = _attackAction.IsPressed(); if (isFiring) { _Weapon.Fire(); } _lookInput = _lookAction.ReadValue<Vector2>(); _moveInput = _moveAction.ReadValue<Vector2>(); // 拆分鼠标Y输入:一部分调基础视角,一部分抵消后坐力 float pitchForBase = _lookInput.y * _RotationSpeed; float pitchForRecoil = 0f; // 当有活跃后坐力(或正在开火),且玩家在下拉鼠标时(假设y负为下拉,根据你的输入映射调整) if ((isFiring || _targetRecoil.y > 0.1f) && _lookInput.y < 0) { // 把下拉鼠标的输入用来抵消后坐力,不再修改基础视角 pitchForRecoil = -_lookInput.y * _RotationSpeed * _recoilCounteractMultiplier; pitchForBase = 0f; } _yawVelocity = _lookInput.x * _RotationSpeed; _pitchVelocity = pitchForBase; // 用抵消输入直接降低后坐力目标值 if (pitchForRecoil > 0) { _targetRecoil.y -= pitchForRecoil * Time.deltaTime * _recoilNeutralizeSpeed; _targetRecoil.y = Mathf.Max(_targetRecoil.y, 0f); // 避免后坐力变成负的(除非你要支持过度抵消) } CameraRecoil(); CameraRotation(); Move(); } // 以下两个方法和你原来的逻辑基本一致,不需要改 private void CameraRecoil() { _targetRecoil = Vector3.Lerp(_targetRecoil, Vector3.zero, 10f * Time.deltaTime); _currentRecoil = Quaternion.Slerp(_currentRecoil, Quaternion.Euler(_targetRecoil * (_isAiming ? _Weapon.AimedCameraRecoil : 1)), 5f * Time.deltaTime); } private void CameraRotation() { _cameraTargetPitch += _pitchVelocity; _cameraTargetPitch = ClampAngle(_cameraTargetPitch, _bottomClamp, _topClamp); _Head.localRotation = Quaternion.Euler(_cameraTargetPitch, 0.0f, 0.0f) * _currentRecoil; transform.Rotate(Vector3.up * _yawVelocity); } private static float ClampAngle(float lfAngle, float lfMin, float lfMax) { if (lfAngle < -360f) lfAngle += 360f; if (lfAngle > 360f) lfAngle -= 360f; return Mathf.Clamp(lfAngle, lfMin, lfMax); } public void ApplyRecoil(Vector2 recoil) { // 确保recoil.y是正的(对应视角上抬,根据你的武器配置调整) _targetRecoil += new Vector3(recoil.y, Random.Range(-recoil.x, recoil.x), -0.1f); }
关键修改点
- 拆分鼠标Y输入:当玩家在开火且下拉鼠标时,这部分输入会被用来直接降低
_targetRecoil的俯仰分量,不再计入基础俯仰角 - 新增手感参数:
_recoilCounteractMultiplier可以调整抵消后坐力的灵敏度,_recoilNeutralizeSpeed控制输入抵消后坐力的速度,多调几次就能找到舒服的手感
方案二:把后坐力作为独立偏移量(更直观的逻辑)
这个方案把后坐力改成一个独立的俯仰偏移量,玩家下拉鼠标直接减少这个偏移,完全不碰基础俯仰角,逻辑更清晰:
修改后的核心代码
// 新增变量:独立的后坐力俯仰偏移 private float _recoilPitchOffset = 0f; // 新增手感参数 [SerializeField] private float _recoilStrength = 5f; [SerializeField] private float _recoilCounteractSpeed = 15f; private void Update() { _isAiming = _aimAction.IsPressed(); _isSprinting = _sprintAction.IsPressed(); bool isFiring = _attackAction.IsPressed(); if (isFiring) { _Weapon.Fire(); // 开火时直接施加后坐力偏移 _recoilPitchOffset += _Weapon.RecoilPitch * _recoilStrength * Time.deltaTime; } _lookInput = _lookAction.ReadValue<Vector2>(); _moveInput = _moveAction.ReadValue<Vector2>(); _yawVelocity = _lookInput.x * _RotationSpeed; // 处理后坐力抵消和基础视角调整 if (_recoilPitchOffset > 0.05f && _lookInput.y < 0) { // 下拉鼠标时,只减少后坐力偏移,不碰基础视角 _recoilPitchOffset -= -_lookInput.y * _RotationSpeed * _recoilCounteractSpeed * Time.deltaTime; _recoilPitchOffset = Mathf.Max(_recoilPitchOffset, 0f); } else { // 没有后坐力需要抵消时,正常调整基础视角 _cameraTargetPitch += _lookInput.y * _RotationSpeed; _cameraTargetPitch = ClampAngle(_cameraTargetPitch, _bottomClamp, _topClamp); } // 后坐力自动回弹 _recoilPitchOffset = Mathf.Lerp(_recoilPitchOffset, 0f, 10f * Time.deltaTime); CameraRotation(); Move(); } private void CameraRotation() { // 最终视角 = 基础俯仰角 + 后坐力偏移 float finalPitch = _cameraTargetPitch + _recoilPitchOffset; finalPitch = ClampAngle(finalPitch, _bottomClamp, _topClamp); _Head.localRotation = Quaternion.Euler(finalPitch, 0f, 0f); transform.Rotate(Vector3.up * _yawVelocity); }
这个方案的优势
逻辑更直白:后坐力是临时的视角上抬偏移,玩家下拉鼠标直接“抵消”这个偏移,完全不会影响基础俯仰角。后坐力消失后,视角自动回到原来的基础位置,不需要额外处理回弹的冲突。
最后提几个手感优化小技巧
- 不同武器可以单独配置
_recoilCounteractMultiplier或_recoilStrength,比如狙击枪的后坐力大但抵消灵敏度更高,冲锋枪相反 - 可以给后坐力的抵消加一点“惯性”,比如让
_recoilPitchOffset的减少不是瞬间的,而是有个小 lerp,手感会更自然 - 测试时记得把
ClampAngle的上下限调合理,避免后坐力+基础视角超出限制导致视角跳变
两种方案都能解决你的问题,选哪个看你更习惯哪种逻辑~ 有手感上的小问题再调参数就行!




