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

Unity C#实现相机往复晃动且支持玩家控制(模拟受击后坐力)

嘿,我懂你要的这种带玩家控制权的后坐力晃动有多棘手——可比单纯的炮塔往复旋转复杂多了!针对你描述的需求,我整理了几个关键思路和解决方向,帮你完善这个效果:

核心实现思路拆解

你要的「先向右上方晃动→再向左下方晃动→用Sin曲线逐步回归中心」+「保留玩家相机控制权」的效果,核心是把玩家基础控制和后坐力偏移拆分开处理,再通过阶段动画实现预期的晃动节奏。

1. 分离相机的旋转层级

别直接修改相机的最终旋转值,把它拆成两个独立部分:

  • 玩家控制的基础旋转:比如鼠标拖动产生的Yaw(左右)、Pitch(上下),这部分要实时响应玩家输入
  • 后坐力的临时偏移旋转:受攻击时产生的晃动效果,作为叠加层作用在基础旋转之上

最终相机的旋转 = 基础旋转 * 后坐力偏移旋转(注意Unity里Quaternion乘法顺序是从右往左叠加,别搞反了!)

2. 阶段式后坐力动画实现

把你要的晃动流程分成三个阶段,用计时器来驱动状态切换:

  • 阶段1:右上晃动:在0~0.1秒内,让后坐力偏移的Yaw/Pitch线性增加到目标峰值(比如Yaw+5°、Pitch+3°)
  • 阶段2:左下晃动:在0.1~0.25秒内,从右上峰值线性过渡到左下峰值(Yaw-5°、Pitch-3°)
  • 阶段3:Sin曲线回归:从0.25秒开始,用Mathf.Sin()生成波动,同时配合衰减系数让振幅慢慢降到0,避免生硬回归

3. 玩家输入与后坐力的融合示例(Unity伪代码)

下面是一个可参考的实现框架,能保证玩家输入实时生效,同时后坐力叠加在上面:

// 存储玩家的基础旋转(不受后坐力影响)
Vector2 baseCameraRotation;
// 后坐力带来的临时角度偏移
Vector2 recoilOffset;
// 后坐力动画计时器(>0时触发动画)
float recoilTimer = 0f;

// 可调整的后坐力参数
[Header("Recoil Settings")]
public float phase1Duration = 0.1f;   // 右上晃动时长
public float phase2Duration = 0.15f;  // 左下晃动时长
public float phase3Duration = 0.5f;   // 回归时长
public Vector2 recoilPeakRightUp = new Vector2(5f, 3f);  // 右上偏移角度
public Vector2 recoilPeakLeftDown = new Vector2(-5f, -3f); // 左下偏移角度
public float mouseSensitivity = 2f;
public float minPitch = -80f;
public float maxPitch = 80f;

void Update()
{
    // 1. 处理玩家输入,更新基础旋转
    baseCameraRotation.x += Input.GetAxis("Mouse X") * mouseSensitivity;
    baseCameraRotation.y -= Input.GetAxis("Mouse Y") * mouseSensitivity;
    baseCameraRotation.y = Mathf.Clamp(baseCameraRotation.y, minPitch, maxPitch);

    // 2. 驱动后坐力阶段动画
    if (recoilTimer > 0f)
    {
        recoilTimer -= Time.deltaTime;
        
        if (recoilTimer > phase2Duration)
        {
            // 阶段1:从初始状态过渡到右上峰值
            float progress = 1 - (recoilTimer - phase2Duration) / phase1Duration;
            recoilOffset = Vector2.Lerp(Vector2.zero, recoilPeakRightUp, progress);
        }
        else if (recoilTimer > 0f)
        {
            // 阶段2:从右上峰值过渡到左下峰值
            float progress = 1 - recoilTimer / phase2Duration;
            recoilOffset = Vector2.Lerp(recoilPeakRightUp, recoilPeakLeftDown, progress);
        }
        else
        {
            // 阶段3:Sin曲线波动+振幅衰减,回归中心
            float fadeProgress = Mathf.Clamp01((phase3Duration + recoilTimer) / phase3Duration);
            float sinWave = Mathf.Sin((-recoilTimer) * Mathf.PI * 2 / phase3Duration);
            recoilOffset = recoilPeakLeftDown * sinWave * fadeProgress;
        }
    }

    // 3. 计算最终相机旋转
    Quaternion baseQuat = Quaternion.Euler(baseCameraRotation.y, baseCameraRotation.x, 0f);
    Quaternion recoilQuat = Quaternion.Euler(recoilOffset.y, recoilOffset.x, 0f);
    transform.rotation = baseQuat * recoilQuat;
}

// 触发后坐力的方法(角色受攻击时调用)
public void TriggerHitRecoil()
{
    recoilTimer = phase1Duration + phase2Duration;
}

4. 常见问题的排查方向

如果你已经实现了晃动但有异常,大概率是这几个点出了问题:

  • 玩家输入被后坐力覆盖:确保基础旋转是独立存储的,后坐力只是叠加偏移,不是直接修改相机的旋转值
  • 回归时抖动/生硬:Sin函数的周期要和回归时长匹配,同时用衰减系数让振幅逐步降为0,避免突然跳变
  • 晃动方向不符合预期:检查Quaternion的乘法顺序,baseQuat * recoilQuat是在玩家控制的局部空间叠加后坐力,反过来会变成世界空间的晃动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:42:41