后坐力效果的实现方式
该组件最终修改的是 AFmPlayerController::RotationInput 属性来实现后坐力效果。
核心修改位置
1. FPP后坐力应用(第407-419行)
const FVector DeltaRecoilRotationInVec = CurrentRecoilRotation - LastFrameRecoilRotation;
const FRotator DeltaRecoilRotation(DeltaRecoilRotationInVec.Y, DeltaRecoilRotationInVec.Z, DeltaRecoilRotationInVec.X);
PlayerCtrl->RotationInput += DeltaRecoilRotation;2. TPP后坐力应用
射击中(第1178-1180行):
PlayerCtrl->RotationInput.Yaw += DeltaYaw;
PlayerCtrl->RotationInput.Pitch += DeltaPitch;恢复中(第1148-1158行):
playerCtrl->RotationInput.Yaw -= yaw;
playerCtrl->RotationInput.Pitch -= dPitch;3. Roll校正(第456行)
PlayerCtrl->RotationInput += DeltaRecoilRotation;RotationInput 的作用机制
flowchart LR A[后坐力计算] --> B[RotationInput<br/>累积增量] B --> C[PlayerController<br/>每帧处理输入] C --> D[应用到相机旋转<br/>CameraManager] D --> E[相机视角变化<br/>后坐力效果] style B fill:#ff9,stroke:#333,stroke-width:3px
RotationInput 是 APlayerController 的成员变量,类型为 FRotator,用于存储待应用的旋转输入增量。
工作原理
| 阶段 | 操作 | 说明 |
|---|---|---|
| 计算 | RotationInput += DeltaRecoilRotation | 将后坐力增量累加到输入缓冲区 |
| 应用 | 引擎自动处理 | APlayerController::UpdateRotation() 每帧读取并应用 |
| 效果 | 相机旋转 | 相机根据 RotationInput 调整朝向 |
为什么使用 RotationInput?
优势
-
与玩家输入统一处理
- 玩家鼠标输入 →
RotationInput - 后坐力效果 →
RotationInput - 两者自然融合,不会冲突
- 玩家鼠标输入 →
-
自动同步到相机
APlayerCameraManager会自动读取并应用- 无需手动修改相机旋转
-
支持输入叠加
// 玩家向下压枪 + 后坐力向上 = 相互抵消 RotationInput.Pitch += PlayerInput; // 玩家输入 RotationInput.Pitch += RecoilDelta; // 后坐力
数据流向
sequenceDiagram participant W as Weapon participant C as RecoilComponent participant P as PlayerController participant Cam as CameraManager W->>C: OnWeaponFireStart() C->>C: 计算后坐力增量<br/>DeltaRecoilRotation loop 每帧 C->>P: RotationInput += Delta P->>Cam: UpdateRotation() Cam->>Cam: 应用到相机视角 end W->>C: OnWeaponFireStop() C->>C: 进入恢复状态 C->>P: RotationInput -= RecoverAmount
总结
该组件通过修改 AFmPlayerController::RotationInput 实现后坐力效果:
- 本质:将后坐力伪装成”玩家输入”
- 优势:与玩家真实输入统一处理,自然融合
- 效果:相机视角根据后坐力增量自动调整
这是一种非侵入式的设计,不需要直接修改相机或角色旋转,而是利用引擎现有的输入处理机制。
后坐力曲线提供的信息
- 基于射击次数的后坐力数值:曲线的横轴是射击次数(
ShotCount),纵轴是后坐力向量(FVector)。这意味着后坐力会随着连续射击的次数而变化(通常模拟后坐力累积或模式)。 - 垂直和水平分量:返回的
FVector的 Y 分量通常代表垂直后坐力,Z 分量代表水平后坐力。 - 最大后坐力参考:通过读取曲线在弹匣最大弹药量处的值,可以获取该武器满弹匣射击时的最大后坐力累积值,用于判断后坐力回复逻辑。
- 随机范围:
RotationRangeCurve提供了后坐力的随机变化范围,用于增加后坐力的不可预测性。
回答用户的问题:
用户问的是“后坐力曲线提供了什么信息”。
根据代码分析,后坐力曲线(RotationCurve)主要提供了每一发子弹射击时产生的标准后坐力旋转值。这个值是基于当前的射击次数(ShotCount)从曲线中采样得到的。
具体来说:
- 输入:射击次数(
ShotCount)。 - 输出:一个
FVector,其中:- Y 分量:代表垂直方向的后坐力。
- Z 分量:代表水平方向的后坐力。
- 用途:
- 计算当前射击相对于上一发射击的后坐力差值 (
Diff)。 - 这个差值会经过各种系数(如后坐力控制属性、状态乘数、随机范围等)的调整,最终应用到玩家的视角旋转上。
- 用于计算最大后坐力,以决定后坐力回复的行为。
- 计算当前射击相对于上一发射击的后坐力差值 (
此外,还有一个 RotationRangeCurve,它提供了后坐力的随机变化范围,用于让每一发的后坐力在标准值附近有一定的随机波动。
构建回答:
我将直接回答用户的问题,指出后坐力曲线提供了基于射击次数的标准后坐力向量,并解释其分量含义和用途。
根据提供的代码,后坐力曲线(RotationCurve)主要提供了基于射击次数的标准后坐力旋转值。
具体来说,它提供了以下信息:
-
输入与输出:
- 输入:当前的射击次数(
ShotCount)。 - 输出:一个
FVector向量,代表该发子弹的标准后坐力旋转。
- 输入:当前的射击次数(
-
向量分量的含义:
- Y 分量:代表垂直后坐力(Pitch)。
- Z 分量:代表水平后坐力(Yaw)。
-
主要用途:
- 计算单发增量:在
GetShotRecoilRotation函数中,代码通过计算当前射击和上一发射击在曲线上的差值 (Diff = ShotStandardRotation - PrevShotStandardRotation),来获得这一发子弹产生的后坐力增量。这个增量随后会乘以各种系数(如后坐力控制属性、蹲下/趴下乘数、随机范围等)应用到玩家视角上。 - 计算最大后坐力:在
ShouldWholeReturn函数中,通过读取曲线在弹匣最大弹药量处的值,可以获取该武器满弹匣射击时的最大后坐力累积值,用于判断后坐力回复逻辑(例如判断当前后坐力是否在预期范围内)。
- 计算单发增量:在
此外,代码中还使用了 RotationRangeCurve,它提供了后坐力的随机变化范围,用于让每一发的后坐力在标准值附近有一定的随机波动,增加真实感。
后坐力恢复打断
[任务规划完成]
从代码中,我可以看到:
- 在
Input_FsmInputMouseChanged函数中有打断逻辑:
void UCharacterWeaponRecoilComponent::Input_FsmInputMouseChanged(float YawInput, float PitchInput)
{
if (FppRecoilState == EFppRecoilState::Returning && !mFppRecoilReturn.IsStable())
{
FRecoilState RecoilState;
if (GetRecoilState(RecoilState) && RecoilState.bReturnInterruptByInput && YawInput + PitchInput > RecoilState.ReturnInterruptThreshold)
{
mFppRecoilReturn.SetStableExceptRoll();
}
}
else if (FppRecoilState == EFppRecoilState::Shooting || FppRecoilState == EFppRecoilState::Holding)
{
AccumulateFireManualRotation += FRotator(PitchInput, YawInput, 0);
}
}这个函数处理了玩家输入打断后坐力恢复的逻辑。
让我详细分析这个逻辑:
-
触发条件:
- 当前状态必须是
EFppRecoilState::Returning(恢复状态) mFppRecoilReturn不稳定(即还在恢复过程中)- 配置允许打断(
RecoilState.bReturnInterruptByInput为 true) - 玩家输入超过阈值(
YawInput + PitchInput > RecoilState.ReturnInterruptThreshold)
- 当前状态必须是
-
打断行为:
- 调用
mFppRecoilReturn.SetStableExceptRoll(),这会停止 Pitch 和 Yaw 的恢复,但保留 Roll 的恢复
- 调用
-
其他情况:
- 如果在射击或保持状态,玩家的输入会被记录到
AccumulateFireManualRotation
- 如果在射击或保持状态,玩家的输入会被记录到
我注意到打断逻辑需要满足多个严格条件。玩家必须在后坐力恢复阶段,且输入强度超过预设阈值。这种设计确保了只有在玩家主动且显著地移动视角时,才会中断后坐力恢复过程。
根据代码,后坐力回复打断的逻辑主要在 Input_FsmInputMouseChanged 函数中实现:
打断逻辑核心代码
void UCharacterWeaponRecoilComponent::Input_FsmInputMouseChanged(float YawInput, float PitchInput)
{
if (FppRecoilState == EFppRecoilState::Returning && !mFppRecoilReturn.IsStable())
{
FRecoilState RecoilState;
if (GetRecoilState(RecoilState) &&
RecoilState.bReturnInterruptByInput &&
YawInput + PitchInput > RecoilState.ReturnInterruptThreshold)
{
mFppRecoilReturn.SetStableExceptRoll();
}
}
else if (FppRecoilState == EFppRecoilState::Shooting ||
FppRecoilState == EFppRecoilState::Holding)
{
AccumulateFireManualRotation += FRotator(PitchInput, YawInput, 0);
}
}打断流程图
flowchart TD A[玩家鼠标输入] --> B{当前状态判断} B -->|Returning状态| C{是否还在恢复中?} B -->|Shooting/Holding状态| D[记录到AccumulateFireManualRotation] C -->|是| E{配置允许打断?} C -->|否| F[不处理] E -->|bReturnInterruptByInput = true| G{输入超过阈值?} E -->|false| F G -->|YawInput + PitchInput > Threshold| H[调用SetStableExceptRoll] G -->|否| F H --> I[停止Pitch和Yaw恢复] I --> J[保留Roll继续恢复] style H fill:#ff6b6b style I fill:#ffd93d style J fill:#6bcf7f
详细说明
1. 触发条件(必须同时满足)
| 条件 | 说明 |
|---|---|
| 状态检查 | FppRecoilState == EFppRecoilState::Returning |
| 恢复未完成 | !mFppRecoilReturn.IsStable() |
| 配置允许 | RecoilState.bReturnInterruptByInput == true |
| 输入强度 | YawInput + PitchInput > RecoilState.ReturnInterruptThreshold |
2. 打断行为
调用 mFppRecoilReturn.SetStableExceptRoll() 后:
- ✅ 停止 Pitch 恢复:垂直后坐力不再自动回正
- ✅ 停止 Yaw 恢复:水平后坐力不再自动回正
- ⚠️ 保留 Roll 恢复:Roll 角度继续校正(避免相机倾斜累积)
3. 设计意图
graph LR A[射击停止] --> B[进入Holding状态] B --> C[保持一段时间] C --> D[进入Returning状态] D --> E[自动恢复后坐力] E --> F{玩家主动输入?} F -->|是| G[打断恢复<br/>玩家接管控制] F -->|否| H[继续自动恢复] style G fill:#ff6b6b
核心思想:
- 后坐力恢复是为了让相机自动回到射击前的位置
- 但如果玩家主动移动鼠标(压枪或瞄准其他目标),说明玩家想自己控制相机
- 此时应该立即停止自动恢复,让玩家完全接管相机控制
- Roll 例外:Roll 角度是异常倾斜,玩家通常不会主动调整,所以继续自动校正
4. 输入记录
在 Shooting 或 Holding 状态下,玩家输入会被记录:
AccumulateFireManualRotation += FRotator(PitchInput, YawInput, 0);这个累积值用于:
- 计算射击过程中的总旋转变化
- 在
CalculateWholeDiff()中判断是否应该完全恢复后坐力 - 区分”后坐力导致的旋转”和”玩家主动输入导致的旋转”
5. 相关配置参数
在 FRecoilState 结构中:
bool bReturnInterruptByInput; // 是否允许输入打断恢复
float ReturnInterruptThreshold; // 打断的输入阈值这些参数可以在武器配置中针对不同武器或瞄准状态进行定制。