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

