You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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);
}

关键修改点

  1. 拆分鼠标Y输入:当玩家在开火且下拉鼠标时,这部分输入会被用来直接降低_targetRecoil的俯仰分量,不再计入基础俯仰角
  2. 新增手感参数_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);
}

这个方案的优势

逻辑更直白:后坐力是临时的视角上抬偏移,玩家下拉鼠标直接“抵消”这个偏移,完全不会影响基础俯仰角。后坐力消失后,视角自动回到原来的基础位置,不需要额外处理回弹的冲突。


最后提几个手感优化小技巧

  1. 不同武器可以单独配置_recoilCounteractMultiplier_recoilStrength,比如狙击枪的后坐力大但抵消灵敏度更高,冲锋枪相反
  2. 可以给后坐力的抵消加一点“惯性”,比如让_recoilPitchOffset的减少不是瞬间的,而是有个小 lerp,手感会更自然
  3. 测试时记得把ClampAngle的上下限调合理,避免后坐力+基础视角超出限制导致视角跳变

两种方案都能解决你的问题,选哪个看你更习惯哪种逻辑~ 有手感上的小问题再调参数就行!

火山引擎 最新活动