UE5 增强输入 Trigger与Modifier

InputMappingContext 和 InputAction 都设置有 Trigger 、 Modifier

执行顺序:

  1. InputMappingContext Trigger
  2. InputMappingContext Modifier
  3. InputAction Trigger
  4. InputAction Modifier

Trigger

以下的 ActuationThresholdTapReleaseTimeThreshold 等为代码中的变量命名,在编辑器 InputAction 面板中有对应的设置。

  • Down :超过 ActuationThreshold 后 -> 触发n次

  • Pressed :超过 ActuationThreshold 后 -> 触发1次

  • Released :超过 ActuationThreshold 后 + 松开(值小于 ActuationThreshold) -> 触发一次

  • Tap :超过 ActuationThreshold 后 + 点按时间小于 TapReleaseTimeThreshold -> 触发一次

  • Pulse :超过 ActuationThreshold 后 + 触发次数小于 TriggerLimit + 输入时间大于 间隔 * (首次是否触发 ? 触发次数 : 触发次数 + 1) -> 触发

    • Completed 仅在达到重复限制或触发后立即释放输入时触发。否则,当释放输入时会触发 Canceled。
  • Hold :超过 ActuationThreshold 后 + 输入时间大于 HoldTimeThreshold + (是否首次触发 || 非仅一次性) -> 触发

  • Hold And Release :超过 ActuationThreshold 后 + 输入时间大于 HoldTimeThreshold + 松开(值小于 ActuationThreshold) -> 触发

  • ChordAction :仅由另一个输入动作触发

    • 该触发器本身的 ActuationThreshold 并不起作用,也符合文档中的说法:

      The one exception to this rule is the “Chorded Action” Input Trigger, which is only triggered with another Input Action. By default, any user activity on an input will trigger on every tick.

      此规则的一个例外是”和弦动作”输入触发器,该触发器仅通过另一个输入动作触发。默认情况下,输入上的任意用户活动都会在每个更新函数上触发。

      1
      2
      3
      4
      5
      6
      ETriggerState UInputTriggerChordAction::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime)
      {
      // Inherit state from the chorded action
      const FInputActionInstance* EventData = PlayerInput->FindActionInstanceData(ChordAction);
      return EventData ? EventData->TriggerStateTracker.GetState() : ETriggerState::None;
      }
    • 如果只单独使用 ChordAction 触发器,一旦触发,后续将输入值慢慢调整为0(例如轻推手柄摇杆),也仍然会继续触发。

      IMC_Test
      IA_Test0

      单独使用 ChordAction 触发器

      IA_Test1_Incorrect

  • Combo :取消 Combo 的输入动作未触发 + Combo 按顺序输入 + 当前输入动作的输入时间小于对应的 TimeToPressKey + 已完成 Combo 中的所有输入动作 -> 触发1次

    • 不会检查第一个输入动作的 TimeToPressKey

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      // Reset if we take too long to hit the action
      if (CurrentComboStepIndex > 0)
      {
      CurrentTimeBetweenComboSteps += DeltaTime;
      if (CurrentTimeBetweenComboSteps >= ComboActions[CurrentComboStepIndex].TimeToPressKey)
      {
      CurrentComboStepIndex = 0;
      CurrentAction = ComboActions[CurrentComboStepIndex].ComboStepAction; // Reset for fallthrough
      }
      }

类型

输入触发器分为三种类型:

  • 显式(Explicit) 类型将导致输入在输入触发器成功时成功。
  • 隐式(Implicit) 类型将导致输入仅在输入触发器和所有其他隐式类型输入触发器都成功时成功。
  • 阻碍(Blocker) 类型将导致输入在输入触发器成功时失败。

官网文档中有关于每种触发器类型在针对其他触发器类型的情形下如何交互的逻辑示例,但感觉说得不够详细:

隐式 == 0,显式 == 0 - 始终触发,除非值为0

Implicits == 0, Explicits == 0 - Always fires, unless the value is 0.

在多轮询问 Bing Chat 后,记录下算是比较能信服的说法:

在增强型输入系统中,每个输入动作都可以有多个触发器,这些触发器可以是显式的、隐式的或阻碍的。

当我们说”隐式 == 0,显式 > 0”时,在这里,”0”和”>0”是开发者在一个特定的输入动作(InputAction)下创建的触发器的数量。”隐式”和”显式”是指触发器的类型。

以下四种情况描述了不同类型和数量的触发器如何影响输入动作的触发:

  • 隐式 == 0,显式 == 0:这意味着没有任何触发器,输入动作将始终触发,除非输入值为0。
  • 隐式 == 0,显式 > 0:这意味着只有显式触发器,只要有一个显式触发器被触发,输入动作就会被触发。
  • 隐式 > 0,显式 == 0:这意味着只有隐式触发器,所有隐式触发器都必须被触发,输入动作才会被触发。
  • 隐式 > 0,显式 > 0:这意味着既有隐式触发器又有显式触发器,所有隐式触发器和至少一个显式触发器必须被触发,输入动作才会被触发。

其实只有当你需要一个输入动作添加多个触发器或是自定义触发器时,才需要考虑触发器的类型。

Modifier

  • DeadZone

    LowerThreshold -> UpperThreshold 范围内的输入值将从 0 -> 1 重新映射。

    类型:

    • Axis(轴):单独计算每个维度,用于处理一维输入,如键盘按键。
    • Radial(径向):用于处理二维输入,如手柄摇杆。
  • Negate :对特定输入轴的值取反。

  • SwizzleAxis :按指定顺序交换轴的值。

  • Scalar :乘以标量来缩放输入值。可以用来调整互动的灵敏度。

  • ScaleByDeltaTime :乘以 DeltaTime 来缩放输入值。

  • FOVScaling : 根据玩家相机的 FOV 值来缩放输入值。

  • ToWorldSpace

    自动将输入操作值内的轴转换为世界空间,允许将结果直接插入到采用世界空间值的函数中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    FInputActionValue UInputModifierToWorldSpace::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
    {
    FVector Converted = CurrentValue.Get<FVector>();
    switch (CurrentValue.GetValueType())
    {
    case EInputActionValueType::Axis3D:
    // Input Device Z = World Forward (X), Device X = World Right (Y), Device Y = World Up (Z)
    Converted = FVector(Converted.Z, Converted.X, Converted.Y);
    break;
    case EInputActionValueType::Axis2D:
    // Swap axes so Input Device Y axis becomes World Forward (X), Device X becomes World Right (Y)
    Swap(Converted.X, Converted.Y);
    break;
    case EInputActionValueType::Axis1D:
    case EInputActionValueType::Boolean:
    // No conversion required
    break;
    }
    return FInputActionValue(CurrentValue.GetValueType(), Converted);
    }
  • ResponseCurveExponential :根据设置对输入值做对应的次方。

  • ResponseCurveUser :根据用户定义曲线对输入值做对应的次方。

  • Smooth

    在多个帧上平滑输入

参考

作者

DullSword

发布于

2023-11-06

更新于

2024-07-02

许可协议

评论