ue-input-system

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UE Enhanced Input System

UE Enhanced Input 系统

You are an expert in Unreal Engine's Enhanced Input system.
你是Unreal Engine的Enhanced Input系统专家。

Context Check

上下文检查

Read
.agents/ue-project-context.md
before proceeding. Confirm:
  • EnhancedInput
    plugin is listed as enabled
  • Target platforms (affects which modifiers are needed per platform)
  • Whether CommonUI is in use (it manages input mode switching automatically)
  • Whether the project still uses legacy input (migration may be needed)
在继续操作前,请阅读
.agents/ue-project-context.md
。确认:
  • EnhancedInput
    插件已列为启用状态
  • 目标平台(影响各平台所需的修改器类型)
  • 是否使用CommonUI(它会自动管理输入模式切换)
  • 项目是否仍在使用旧版输入系统(可能需要迁移)

Information Gathering

信息收集

Ask the developer: what actions are needed and their value types (Bool/Axis1D/Axis2D/Axis3D), which platforms, any complex input requirements (hold-to-charge, double-tap, combos, chord shortcuts), and whether multiple input modes are required (gameplay vs UI vs vehicle).

询问开发者:需要实现哪些操作及其值类型(Bool/Axis1D/Axis2D/Axis3D)、目标平台、是否有复杂输入需求(按住蓄力、双击、组合键、和弦快捷键),以及是否需要多种输入模式(游戏玩法/UI/载具)。

Enhanced Input Setup

Enhanced Input 设置

Plugin and Module

插件与模块

.uproject
: add
{ "Name": "EnhancedInput", "Enabled": true }
to Plugins.
Build.cs
: add
"EnhancedInput"
to
PublicDependencyModuleNames
.
DefaultInput.ini
:
ini
[/Script/Engine.InputSettings]
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
.uproject
:在Plugins中添加
{ "Name": "EnhancedInput", "Enabled": true }
Build.cs
:在
PublicDependencyModuleNames
中添加
"EnhancedInput"
DefaultInput.ini
:
ini
[/Script/Engine.InputSettings]
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent

UInputAction Asset

UInputAction 资源

UInputAction : UDataAsset
. Create one per logical player action. Key properties (from
InputAction.h
):
cpp
EInputActionValueType ValueType = EInputActionValueType::Boolean;
// Boolean | Axis1D (float) | Axis2D (FVector2D) | Axis3D (FVector)

EInputActionAccumulationBehavior AccumulationBehavior
    = EInputActionAccumulationBehavior::TakeHighestAbsoluteValue;
// TakeHighestAbsoluteValue — highest magnitude wins across all mappings to this action
// Cumulative — all mapping values sum (W + S cancel each other for WASD)

bool bConsumeInput = true;  // blocks lower-priority Enhanced Input mappings to same keys

TArray<TObjectPtr<UInputTrigger>> Triggers;   // applied AFTER per-mapping triggers
TArray<TObjectPtr<UInputModifier>> Modifiers; // applied AFTER per-mapping modifiers
UInputAction : UDataAsset
。为每个逻辑玩家操作创建一个实例。关键属性(来自
InputAction.h
):
cpp
EInputActionValueType ValueType = EInputActionValueType::Boolean;
// Boolean | Axis1D(浮点型) | Axis2D(FVector2D) | Axis3D(FVector)

EInputActionAccumulationBehavior AccumulationBehavior
    = EInputActionAccumulationBehavior::TakeHighestAbsoluteValue;
// TakeHighestAbsoluteValue — 所有映射到该操作的输入中,绝对值最大的生效
// Cumulative — 所有映射值相加(W和S在WASD中会相互抵消)

bool bConsumeInput = true;  // 阻止低优先级的Enhanced Input映射使用相同按键

TArray<TObjectPtr<UInputTrigger>> Triggers;   // 在每个映射的触发器之后应用
TArray<TObjectPtr<UInputModifier>> Modifiers; // 在每个映射的修改器之后应用

UInputMappingContext Asset

UInputMappingContext 资源

UInputMappingContext : UDataAsset
. Maps physical keys to actions.
  • DefaultKeyMappings.Mappings
    TArray<FEnhancedActionKeyMapping>
    of key-to-action entries
  • MappingProfileOverrides
    — per-profile key overrides for player remapping support
  • RegistrationTrackingMode
    :
    Untracked
    (default, first Remove wins) or
    CountRegistrations
    (IMC stays until Remove called N times, safe when multiple systems share it)

UInputMappingContext : UDataAsset
。将物理按键映射到操作。
  • DefaultKeyMappings.Mappings
    — 按键到操作的
    TArray<FEnhancedActionKeyMapping>
    条目
  • MappingProfileOverrides
    — 针对玩家重映射支持的每个配置文件的按键覆盖
  • RegistrationTrackingMode
    :
    Untracked
    (默认,首次移除生效)或
    CountRegistrations
    (IMC需调用N次Remove才会移除,适合多系统共享的场景)

Binding Actions in C++

在C++中绑定操作

SetupPlayerInputComponent

SetupPlayerInputComponent

cpp
// MyCharacter.h — declare assets and handlers
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputMappingContext> DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> JumpAction;

void Move(const FInputActionValue& Value);
void StartJump();
void StopJump();
cpp
// MyCharacter.cpp
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
    if (!EIC) { return; }

    EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
    EIC->BindAction(JumpAction, ETriggerEvent::Started,   this, &AMyCharacter::StartJump);
    EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &AMyCharacter::StopJump);
}

void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    if (APlayerController* PC = Cast<APlayerController>(GetController()))
    {
        if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
                PC->GetLocalPlayer()))
        {
            Sub->AddMappingContext(DefaultMappingContext, 0); // priority 0 = lowest
        }
    }
}
cpp
// MyCharacter.h — 声明资源和处理函数
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputMappingContext> DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
TObjectPtr<UInputAction> JumpAction;

void Move(const FInputActionValue& Value);
void StartJump();
void StopJump();
cpp
// MyCharacter.cpp
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
    if (!EIC) { return; }

    EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
    EIC->BindAction(JumpAction, ETriggerEvent::Started,   this, &AMyCharacter::StartJump);
    EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &AMyCharacter::StopJump);
}

void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    if (APlayerController* PC = Cast<APlayerController>(GetController()))
    {
        if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(
                PC->GetLocalPlayer()))
        {
            Sub->AddMappingContext(DefaultMappingContext, 0); // 优先级0=最低
        }
    }
}

Callback Signatures

回调签名

BindAction
accepts four delegate signatures:
cpp
// No params — press/release without value needed
void AMyCharacter::StartJump() { Jump(); }

// FInputActionValue — for axis values
void AMyCharacter::Move(const FInputActionValue& Value)
{
    const FVector2D Input = Value.Get<FVector2D>();
    AddMovementInput(GetActorForwardVector(), Input.Y);
    AddMovementInput(GetActorRightVector(),   Input.X);
}

// FInputActionInstance — when elapsed/triggered time is needed
void AMyCharacter::OnChargeAttack(const FInputActionInstance& Instance)
{
    const float HeldFor = Instance.GetElapsedTime();    // Started + Ongoing + Triggered
    const float ActiveFor = Instance.GetTriggeredTime(); // Triggered only
}

// Lambda variant
EIC->BindActionValueLambda(InteractAction, ETriggerEvent::Triggered,
    [this](const FInputActionValue& Value) { TryInteract(); });
Storing and removing a binding:
cpp
FEnhancedInputActionEventBinding& B =
    EIC->BindAction(DebugAction, ETriggerEvent::Started, this, &AMyCharacter::DebugToggle);
uint32 Handle = B.GetHandle();
// ...
EIC->RemoveBindingByHandle(Handle);        // remove one binding
EIC->ClearBindingsForObject(this);         // remove all bindings for an object

BindAction
支持四种委托签名:
cpp
// 无参数 — 无需值的按压/释放操作
void AMyCharacter::StartJump() { Jump(); }

// FInputActionValue — 用于轴值
void AMyCharacter::Move(const FInputActionValue& Value)
{
    const FVector2D Input = Value.Get<FVector2D>();
    AddMovementInput(GetActorForwardVector(), Input.Y);
    AddMovementInput(GetActorRightVector(),   Input.X);
}

// FInputActionInstance — 需要获取持续时间/触发时间时使用
void AMyCharacter::OnChargeAttack(const FInputActionInstance& Instance)
{
    const float HeldFor = Instance.GetElapsedTime();    // Started + Ongoing + Triggered状态的持续时间
    const float ActiveFor = Instance.GetTriggeredTime(); // 仅Triggered状态的持续时间
}

// Lambda变体
EIC->BindActionValueLambda(InteractAction, ETriggerEvent::Triggered,
    [this](const FInputActionValue& Value) { TryInteract(); });
存储并移除绑定:
cpp
FEnhancedInputActionEventBinding& B =
    EIC->BindAction(DebugAction, ETriggerEvent::Started, this, &AMyCharacter::DebugToggle);
uint32 Handle = B.GetHandle();
// ...
EIC->RemoveBindingByHandle(Handle);        // 移除单个绑定
EIC->ClearBindingsForObject(this);         // 移除某个对象的所有绑定

Trigger Events (ETriggerEvent)

触发事件(ETriggerEvent)

Bitmask enum from
InputTriggers.h
:
EventState TransitionUse for
Started
None -> Ongoing/TriggeredFirst frame of input; press-once actions
Triggered
*->Triggered, Triggered->TriggeredEvery active frame; continuous movement
Ongoing
Ongoing->OngoingHeld but not yet triggered (charge build-up)
Canceled
Ongoing->NoneReleased before trigger threshold
Completed
Triggered->NoneInput released after triggering; stop continuous actions
Note:
Completed
does not fire if any trigger on the same action reports
Ongoing
that frame.

来自
InputTriggers.h
的位掩码枚举:
事件状态转换适用场景
Started
None -> Ongoing/Triggered输入的第一帧;单次按压操作
Triggered
*->Triggered, Triggered->Triggered输入激活的每帧;持续移动操作
Ongoing
Ongoing->Ongoing按住但尚未触发(蓄力阶段)
Canceled
Ongoing->None在达到触发阈值前释放
Completed
Triggered->None触发后释放输入;停止持续操作
注意:如果同一操作的任何触发器在当前帧报告
Ongoing
,则
Completed
不会触发。

Built-in Triggers

内置触发器

Full parameter listings in
references/input-action-reference.md
.
ClassNameBehavior
UInputTriggerDown
DownEvery frame input exceeds threshold (implicit default)
UInputTriggerPressed
PressedOnce on first actuation; holding does not repeat
UInputTriggerReleased
ReleasedOnce when input drops below threshold after actuation
UInputTriggerHold
HoldAfter
HoldTimeThreshold
s;
bIsOneShot=false
repeats every frame
UInputTriggerHoldAndRelease
Hold And ReleaseOn release after holding
HoldTimeThreshold
s
UInputTriggerTap
TapReleased within
TapReleaseTimeThreshold
s
UInputTriggerRepeatedTap
Repeated TapN taps within
RepeatDelay
(
NumberOfTapsWhichTriggerRepeat=2
for double-tap)
UInputTriggerPulse
PulseRepeatedly at
Interval
s while held; optional
TriggerLimit
UInputTriggerChordAction
Chorded ActionOnly fires while
ChordAction
is active (Implicit type; auto-blocks solo key)
UInputTriggerCombo
Combo (Beta)All
ComboActions
completed in order within
TimeToPressKey
windows
Trigger type rules for multi-trigger evaluation:
Explicit
(default, at least one must fire),
Implicit
(all must fire),
Blocker
(blocks everything if active).

完整参数列表请查看
references/input-action-reference.md
名称行为
UInputTriggerDown
Down输入超过阈值的每帧(隐式默认)
UInputTriggerPressed
Pressed首次触发时执行一次;按住不会重复触发
UInputTriggerReleased
Released触发后输入低于阈值时执行一次
UInputTriggerHold
Hold按住超过
HoldTimeThreshold
秒后触发;
bIsOneShot=false
时每帧重复触发
UInputTriggerHoldAndRelease
Hold And Release按住超过
HoldTimeThreshold
秒后释放时触发
UInputTriggerTap
Tap
TapReleaseTimeThreshold
秒内释放时触发
UInputTriggerRepeatedTap
Repeated Tap
RepeatDelay
时间内完成N次点击(
NumberOfTapsWhichTriggerRepeat=2
为双击)
UInputTriggerPulse
Pulse按住时每隔
Interval
秒重复触发;可选
TriggerLimit
限制次数
UInputTriggerChordAction
Chorded Action仅当
ChordAction
激活时触发(隐式类型;自动阻止单独按键触发)
UInputTriggerCombo
Combo(测试版)
TimeToPressKey
窗口内按顺序完成所有
ComboActions
时触发
多触发器评估的规则:
Explicit
(默认,至少一个触发即可)、
Implicit
(所有必须触发)、
Blocker
(如果激活则阻止所有触发)。

Built-in Modifiers

内置修改器

Applied in array order. Mapping-level modifiers run before action-level modifiers.
ClassNameEffect
UInputModifierDeadZone
Dead ZoneZero input below
LowerThreshold
; remap to 1 at
UpperThreshold
. Types: Axial, Radial, UnscaledRadial
UInputModifierScalar
ScalarMultiply per axis by
FVector Scalar
UInputModifierScaleByDeltaTime
Scale By Delta TimeMultiply by frame DeltaTime
UInputModifierNegate
NegateInvert selected axes (
bX
,
bY
,
bZ
)
UInputModifierSwizzleAxis
Swizzle Input Axis ValuesReorder axes;
YXZ
(default) swaps X/Y — maps 1D key onto Y of Axis2D action
UInputModifierSmooth
SmoothRolling average over recent samples
UInputModifierSmoothDelta
Smooth DeltaSmoothed normalized delta; configurable interpolation (
Lerp
,
Interp_To
, ease curves)
UInputModifierResponseCurveExponential
Response Curve - Exponential`sign(x)*
UInputModifierResponseCurveUser
Response Curve - User DefinedSeparate
UCurveFloat
per axis
UInputModifierFOVScaling
FOV ScalingScale by camera FOV for consistent angular speed across zoom levels
UInputModifierToWorldSpace
To World Space2D axis -> world space (up/down = world X, left/right = world Y)
WASD -> Axis2D recipe (
AccumulationBehavior = Cumulative
):
  • W
    :
    SwizzleAxis(YXZ)
    → Y=+1
  • S
    :
    SwizzleAxis(YXZ)
    +
    Negate(bY)
    → Y=-1
  • D
    : none → X=+1
  • A
    :
    Negate(bX)
    → X=-1
Gamepad stick:
DeadZone(Radial, LowerThreshold=0.2)
per stick mapping.
Mouse look:
[Scalar(0.4,0.4,1), Smooth, FOVScaling]
per mapping.

按数组顺序应用。映射级修改器先于操作级修改器运行。
名称效果
UInputModifierDeadZone
Dead Zone低于
LowerThreshold
的输入置为0;高于
UpperThreshold
的输入映射为1。类型:Axial、Radial、UnscaledRadial
UInputModifierScalar
Scalar按轴乘以
FVector Scalar
UInputModifierScaleByDeltaTime
Scale By Delta Time乘以帧DeltaTime
UInputModifierNegate
Negate反转选定轴(
bX
bY
bZ
UInputModifierSwizzleAxis
Swizzle Input Axis Values重新排序轴;
YXZ
(默认)交换X/Y — 将一维按键映射到Axis2D操作的Y轴
UInputModifierSmooth
Smooth最近样本的滚动平均值
UInputModifierSmoothDelta
Smooth Delta平滑归一化增量;可配置插值方式(
Lerp
Interp_To
、缓动曲线)
UInputModifierResponseCurveExponential
Response Curve - Exponential按轴计算`sign(x)*
UInputModifierResponseCurveUser
Response Curve - User Defined每个轴单独使用
UCurveFloat
UInputModifierFOVScaling
FOV Scaling根据相机FOV缩放,确保不同缩放级别下的角速度一致
UInputModifierToWorldSpace
To World Space将2D轴转换为世界空间(上下=世界X轴,左右=世界Y轴)
WASD -> Axis2D 配置方案
AccumulationBehavior = Cumulative
):
  • W
    SwizzleAxis(YXZ)
    → Y=+1
  • S
    SwizzleAxis(YXZ)
    +
    Negate(bY)
    → Y=-1
  • D
    :无修改 → X=+1
  • A
    Negate(bX)
    → X=-1
游戏手柄摇杆:每个摇杆映射添加
DeadZone(Radial, LowerThreshold=0.2)
鼠标视角:每个映射添加
[Scalar(0.4,0.4,1), Smooth, FOVScaling]

Mapping Context Priority

映射上下文优先级

cpp
// Higher integer = higher priority; wins key conflicts
Subsystem->AddMappingContext(GameplayIMC, 0);
Subsystem->AddMappingContext(VehicleIMC,  1);

// FModifyContextOptions — prevent ghost inputs on switch
FModifyContextOptions Opts;
Opts.bIgnoreAllPressedKeysUntilRelease = true;
Subsystem->AddMappingContext(UIIMC, 2, Opts);

// Remove on mode exit
Subsystem->RemoveMappingContext(VehicleIMC);
When
bConsumeInput = true
on a
UInputAction
(the default), a higher-priority context that maps the same physical key will consume it, blocking all lower-priority bindings to that key from firing. This is intentional: use priority layering and
bConsumeInput
together to prevent input conflicts between modes (e.g., a vehicle context consuming Spacebar so the character's Jump action never fires while driving).
cpp
// 数值越高优先级越高;按键冲突时高优先级生效
Subsystem->AddMappingContext(GameplayIMC, 0);
Subsystem->AddMappingContext(VehicleIMC,  1);

// FModifyContextOptions — 切换时防止幽灵输入
FModifyContextOptions Opts;
Opts.bIgnoreAllPressedKeysUntilRelease = true;
Subsystem->AddMappingContext(UIIMC, 2, Opts);

// 退出模式时移除
Subsystem->RemoveMappingContext(VehicleIMC);
UInputAction
bConsumeInput = true
(默认值)时,映射相同物理按键的高优先级上下文会消耗该按键输入,阻止所有低优先级绑定触发。这是有意设计的:结合优先级分层和
bConsumeInput
可防止不同模式间的输入冲突(例如,载具上下文消耗空格键,这样驾驶时角色的跳跃操作不会触发)。

Split-Screen / Multiple Local Players

分屏/多本地玩家

In split-screen, each local player has their own
UEnhancedInputLocalPlayerSubsystem
. Mapping contexts are per-player — adding a context to one player's subsystem does not affect others. To target a specific player, retrieve their subsystem directly from their
ULocalPlayer
:
cpp
// Access subsystem for a specific local player (e.g., player 2)
if (ULocalPlayer* LP = PlayerController->GetLocalPlayer())
{
    if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LP))
    {
        Sub->AddMappingContext(PlayerTwoIMC, 0);
    }
}

分屏模式下,每个本地玩家拥有自己的
UEnhancedInputLocalPlayerSubsystem
。映射上下文是按玩家独立的 — 向一个玩家的子系统添加上下文不会影响其他玩家。要定位特定玩家,直接从其
ULocalPlayer
获取子系统:
cpp
// 获取特定本地玩家的子系统(例如,玩家2)
if (ULocalPlayer* LP = PlayerController->GetLocalPlayer())
{
    if (auto* Sub = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LP))
    {
        Sub->AddMappingContext(PlayerTwoIMC, 0);
    }
}

Custom Triggers

自定义触发器

Subclass
UInputTrigger
; override
UpdateState_Implementation
returning
ETriggerState::None / Ongoing / Triggered
:
cpp
UCLASS(EditInlineNew, meta=(DisplayName="Double Click"))
class MYGAME_API UDoubleClickTrigger : public UInputTrigger
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trigger Settings")
    float DoubleClickThreshold = 0.3f;
protected:
    virtual ETriggerType GetTriggerType_Implementation() const override
        { return ETriggerType::Explicit; }
    virtual ETriggerState UpdateState_Implementation(
        const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override;
private:
    float LastPressTime = 0.f;
    bool bWasActuated = false;
};

ETriggerState UDoubleClickTrigger::UpdateState_Implementation(
    const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime)
{
    const bool bActuated = IsActuated(ModifiedValue); // helper: magnitude >= ActuationThreshold
    const float Now = PlayerInput->GetWorld()->GetTimeSeconds();
    if (bActuated && !bWasActuated)
    {
        if ((Now - LastPressTime) <= DoubleClickThreshold)
            { LastPressTime = 0.f; bWasActuated = bActuated; return ETriggerState::Triggered; }
        LastPressTime = Now;
    }
    bWasActuated = bActuated;
    return ETriggerState::None;
}
UInputTriggerTimedBase
provides
HeldDuration
and
CalculateHeldDuration
for time-based triggers.

继承
UInputTrigger
;重写
UpdateState_Implementation
并返回
ETriggerState::None / Ongoing / Triggered
cpp
UCLASS(EditInlineNew, meta=(DisplayName="Double Click"))
class MYGAME_API UDoubleClickTrigger : public UInputTrigger
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trigger Settings")
    float DoubleClickThreshold = 0.3f;
protected:
    virtual ETriggerType GetTriggerType_Implementation() const override
        { return ETriggerType::Explicit; }
    virtual ETriggerState UpdateState_Implementation(
        const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override;
private:
    float LastPressTime = 0.f;
    bool bWasActuated = false;
};

ETriggerState UDoubleClickTrigger::UpdateState_Implementation(
    const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime)
{
    const bool bActuated = IsActuated(ModifiedValue); // 辅助函数:幅度 >= ActuationThreshold
    const float Now = PlayerInput->GetWorld()->GetTimeSeconds();
    if (bActuated && !bWasActuated)
    {
        if ((Now - LastPressTime) <= DoubleClickThreshold)
            { LastPressTime = 0.f; bWasActuated = bActuated; return ETriggerState::Triggered; }
        LastPressTime = Now;
    }
    bWasActuated = bActuated;
    return ETriggerState::None;
}
UInputTriggerTimedBase
为基于时间的触发器提供
HeldDuration
CalculateHeldDuration
方法。

Custom Modifiers

自定义修改器

Subclass
UInputModifier
; override
ModifyRaw_Implementation
:
cpp
UCLASS(EditInlineNew, meta=(DisplayName="Clamp Magnitude"))
class MYGAME_API UClampMagnitudeModifier : public UInputModifier
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Settings)
    float MaxMagnitude = 1.0f;
protected:
    virtual FInputActionValue ModifyRaw_Implementation(
        const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override
    {
        FVector V = CurrentValue.Get<FVector>();
        if (V.SizeSquared() > MaxMagnitude * MaxMagnitude)
            V = V.GetSafeNormal() * MaxMagnitude;
        return FInputActionValue(CurrentValue.GetValueType(), V);
    }
};

继承
UInputModifier
;重写
ModifyRaw_Implementation
cpp
UCLASS(EditInlineNew, meta=(DisplayName="Clamp Magnitude"))
class MYGAME_API UClampMagnitudeModifier : public UInputModifier
{
    GENERATED_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Settings)
    float MaxMagnitude = 1.0f;
protected:
    virtual FInputActionValue ModifyRaw_Implementation(
        const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override
    {
        FVector V = CurrentValue.Get<FVector>();
        if (V.SizeSquared() > MaxMagnitude * MaxMagnitude)
            V = V.GetSafeNormal() * MaxMagnitude;
        return FInputActionValue(CurrentValue.GetValueType(), V);
    }
};

Common Mistakes

常见错误

  • Mapping context not added: Add via subsystem in
    BeginPlay
    /after
    Possess
    , not in
    SetupPlayerInputComponent
    (called earlier on some paths).
  • Legacy binding on UEnhancedInputComponent:
    BindAction(FName,...)
    and
    BindAxis
    are
    = delete
    . Compile error. Set
    DefaultInputComponentClass
    in
    DefaultInput.ini
    .
  • Triggered for a button press:
    Triggered
    fires every active frame. Use
    Started
    for press-once,
    Completed
    for release.
  • Completed not firing with Ongoing: If any trigger reports
    Ongoing
    that frame,
    Completed
    is suppressed. Use separate actions for clean press/release events.
  • No dead zone on gamepad sticks: Sticks produce non-zero resting values. Always add
    DeadZone(Radial)
    per stick mapping.
  • Missing SwizzleAxis for WASD-to-2D: Keyboard produces 1D. Without
    SwizzleAxis(YXZ)
    on W/S, forward/backward stays on X and is ignored by Axis2D forward movement.
  • Replicating input actions: Input is client-local. Replicate results (movement, ability activation), not trigger events.
  • MapKey/UnmapKey at runtime for rebinding: These are editor/config-screen helpers. Use subsystem player mappable key APIs or swap contexts instead.
  • Wrong trigger type for intent: Using
    Down
    when
    Hold
    is needed, or
    Triggered
    when
    Started
    is needed. Match trigger type to the interaction pattern:
    Started
    for single press,
    Triggered
    for continuous,
    Hold
    for delayed activation.

  • 未添加映射上下文:在
    BeginPlay
    /
    Possess
    之后通过子系统添加,不要在
    SetupPlayerInputComponent
    中添加(某些流程中会更早调用)。
  • 在UEnhancedInputComponent上使用旧版绑定
    BindAction(FName,...)
    BindAxis
    已被
    = delete
    ,会导致编译错误。在
    DefaultInput.ini
    中设置
    DefaultInputComponentClass
  • 按钮按压触发Triggered
    Triggered
    会在输入激活的每帧触发。单次按压使用
    Started
    ,释放使用
    Completed
  • Ongoing状态下Completed不触发:如果任何触发器在当前帧报告
    Ongoing
    Completed
    会被抑制。为清晰的按压/释放事件使用单独的操作。
  • 游戏手柄摇杆未设置死区:摇杆会产生非零静止值。务必为每个摇杆映射添加
    DeadZone(Radial)
  • WASD转2D轴缺少SwizzleAxis:键盘产生一维输入。如果W/S没有
    SwizzleAxis(YXZ)
    ,前后移动会停留在X轴,被Axis2D的向前移动忽略。
  • 复制输入操作:输入是客户端本地的。复制操作结果(移动、技能激活),而非触发事件。
  • 运行时使用MapKey/UnmapKey进行重映射:这些是编辑器/配置界面的辅助函数。使用子系统的玩家可映射按键API或切换上下文替代。
  • 触发器类型与意图不符:需要
    Hold
    时使用了
    Down
    ,需要
    Started
    时使用了
    Triggered
    。根据交互模式匹配触发器类型:单次按压用
    Started
    ,持续操作用
    Triggered
    ,延迟激活用
    Hold

Legacy Input Migration

旧版输入系统迁移

To migrate from the legacy input system to Enhanced Input: search for
InputComponent->BindAction
and
InputComponent->BindAxis
calls and replace each with
UEnhancedInputComponent::BindAction
. Create a
UInputAction
data asset for every old action name, choosing the appropriate
ValueType
(Boolean for buttons, Axis1D for single-axis, Axis2D for stick/WASD). Create a
UInputMappingContext
asset and add key mappings corresponding to the old
DefaultInput.ini
ActionMappings
/
AxisMappings
entries. Set
DefaultInputComponentClass
in
DefaultInput.ini
and enable the EnhancedInput plugin.
要从旧版输入系统迁移到Enhanced Input:搜索
InputComponent->BindAction
InputComponent->BindAxis
调用,将每个调用替换为
UEnhancedInputComponent::BindAction
。为每个旧操作名称创建
UInputAction
数据资源,选择合适的
ValueType
(按钮用Boolean,单轴用Axis1D,摇杆/WASD用Axis2D)。创建
UInputMappingContext
资源,并添加与旧版
DefaultInput.ini
ActionMappings
/
AxisMappings
条目对应的按键映射。在
DefaultInput.ini
中设置
DefaultInputComponentClass
并启用EnhancedInput插件。

UI Input Mode

UI输入模式

Without CommonUI, manage input modes manually via
APlayerController::SetInputMode()
:
cpp
PC->SetInputMode(FInputModeUIOnly());          // cursor captured by UI, no game input
PC->SetInputMode(FInputModeGameAndUI());       // both UI and game receive input
PC->SetInputMode(FInputModeGameOnly());        // full game input, UI events suppressed
CommonUI automates input routing through
UCommonActivatableWidget
stacks and eliminates most manual
SetInputMode
calls — see
ue-ui-umg-slate
.

如果未使用CommonUI,需通过
APlayerController::SetInputMode()
手动管理输入模式:
cpp
PC->SetInputMode(FInputModeUIOnly());          // 光标被UI捕获,无游戏输入
PC->SetInputMode(FInputModeGameAndUI());       // UI和游戏均可接收输入
PC->SetInputMode(FInputModeGameOnly());        // 完整游戏输入,UI事件被抑制
CommonUI通过
UCommonActivatableWidget
栈自动处理输入路由,可消除大多数手动
SetInputMode
调用 — 请参阅
ue-ui-umg-slate

Related Skills

相关技能

  • ue-gameplay-framework
    — PlayerController input lifecycle,
    Possess
    /
    UnPossess
    ,
    SetupPlayerInputComponent
  • ue-ui-umg-slate
    SetInputMode(FInputModeUIOnly())
    /
    FInputModeGameAndUI()
    , cursor visibility, CommonUI
    UCommonActivatableWidget
    stacks
  • ue-gameplay-abilities
    — binding Enhanced Input actions to GAS ability activation via input ID tags
  • ue-gameplay-framework
    — PlayerController输入生命周期、
    Possess
    /
    UnPossess
    SetupPlayerInputComponent
  • ue-ui-umg-slate
    SetInputMode(FInputModeUIOnly())
    /
    FInputModeGameAndUI()
    、光标可见性、CommonUI
    UCommonActivatableWidget
  • ue-gameplay-abilities
    — 通过输入ID标签将Enhanced Input操作绑定到GAS技能激活