总览
UInputAction 最容易被误解的地方是:它不是“按键按下”的事件,而是一个带值、带触发规则、带运行时状态的动作。IA_Move 是二维向量,IA_Jump 是布尔,IA_Zoom 可以是一维轴,IA_AimDirection 甚至可以是三维向量。
值类型怎么选
| Value Type | 常见用途 | C++ 读取 |
|---|---|---|
| Boolean | 跳跃、交互、开火、确认 | Value.Get<bool>() |
| Axis1D | 油门、刹车、缩放、扳机 | Value.Get<float>() |
| Axis2D | 移动、视角、菜单导航 | Value.Get<FVector2D>() |
| Axis3D | VR/飞行/调试自由移动 | Value.Get<FVector>() |
不要为了省事把所有 Action 都设成 Boolean。只要你需要方向、强度、模拟量、鼠标 delta,就应该用 Axis。
事件怎么理解
ETriggerEvent 不是简单的 Pressed/Released。源码里把事件定义成 Trigger State 的转换:
| Event | 大白话 | 常见用法 |
|---|---|---|
| Started | 输入开始被评估 | 起手音效、蓄力开始 |
| Ongoing | 正在等待触发条件 | Hold 未达时长的进度条 |
| Triggered | 条件满足 | 移动每帧、开火、蓄力成功 |
| Completed | 从 Triggered 回到 None | 松开跳跃、松开移动 |
| Canceled | Started/Ongoing 但没成功 | Hold 不够时间就松手 |
没有 Trigger 时,Enhanced Input 使用类似 Down 的默认行为:输入超过阈值就 Triggered,松开后 Completed。
Value 和 Instance
简单动作绑定 FInputActionValue 足够:
void AMyCharacter::Move(const FInputActionValue& Value)
{
const FVector2D Axis = Value.Get<FVector2D>();
AddMovementInput(GetActorForwardVector(), Axis.Y);
AddMovementInput(GetActorRightVector(), Axis.X);
}
如果你需要触发时长、当前事件、源 Action,就绑定 FInputActionInstance:
void AMyCharacter::ChargeAttack(const FInputActionInstance& Instance)
{
const float HeldSeconds = Instance.GetElapsedTime();
const ETriggerEvent Event = Instance.GetTriggerEvent();
const float ChargeAlpha = FMath::Clamp(HeldSeconds / 1.2f, 0.0f, 1.0f);
UpdateChargeUI(ChargeAlpha, Event);
}
源码里 FInputActionInstance::GetValue() 还有一个重要约束:如果当前事件不是 Triggered,当前值可能会是该类型的零值。进度条更适合看 ElapsedTime 或 Trigger 自己的状态,而不是只看 Value。
使用案例:跳跃
跳跃不要绑 Triggered,因为按住空格时它可能每帧触发。更稳定的写法是:
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
这样“按下开始跳,松开停止跳跃延长”符合 Character 的预期。
使用案例:蓄力攻击
蓄力攻击可以给 Action 加 Hold Trigger。Started 打开 UI,Ongoing 更新进度,Triggered 发出蓄满攻击,Canceled 取消或释放普通攻击。
EIC->BindAction(ChargeAction, ETriggerEvent::Started, this, &AMyCharacter::BeginCharge);
EIC->BindAction(ChargeAction, ETriggerEvent::Ongoing, this, &AMyCharacter::UpdateCharge);
EIC->BindAction(ChargeAction, ETriggerEvent::Triggered, this, &AMyCharacter::CommitChargedAttack);
EIC->BindAction(ChargeAction, ETriggerEvent::Canceled, this, &AMyCharacter::CancelCharge);
Accumulation 行为
UInputAction 有 AccumulationBehavior。默认 TakeHighestAbsoluteValue 会取绝对值最大的输入来源;Cumulative 会累加输入,适合 WASD 这种 W 和 S 同时按下应该互相抵消的动作。移动 Action 如果出现“W+S 仍然向前”,先看这里。
常见坑
- Jump 用
Triggered,导致按住空格每帧调用 Jump。 - Hold 只绑
Triggered,没有处理Canceled,UI 卡住。 - Axis2D Action 只绑定 Mouse X,忘了 Mouse Y,结果 Look 只有横向。
- 同一个 Action 既给键鼠又给手柄轴,没处理灵敏度和死区差异。
- 把
Completed当释放事件用在所有 Trigger 上,但某些 Trigger 的 Completed 语义和直觉不同。
源码依据
InputActionValue.h 封装 Boolean、Axis1D、Axis2D、Axis3D。InputTriggers.h 定义 ETriggerState 和 ETriggerEvent,并注明 Completed 和 Canceled 的状态转换。FInputActionInstance 保存 TriggerEvent、Value、ElapsedTime、TriggeredTime、SourceAction、Triggers 和 Modifiers。
源码路径索引
EnhancedInput/Public/InputAction.hEnhancedInput/Public/InputActionValue.hEnhancedInput/Public/InputTriggers.hEnhancedInput/Public/EnhancedPlayerInput.h