关于 ue-input-system
面向 UE 5.8 项目的 Enhanced Input 开发技能,说明输入资源、C++ 绑定及运行时配置。还覆盖触发器、修饰器、映射上下文、用户重绑定和输入模式过滤。
主要功能
- 01Input Action 与映射上下文配置
- 02C++ 输入事件绑定与移除
- 03触发事件及内置触发器
- 04输入修饰器与轴值变换
- 05上下文优先级及运行时重绑定
适用场景
- 01为角色动作绑定按键和输入事件
- 02配置按住、轻击或连击触发行为
- 03调整摇杆死区、灵敏度及轴映射
- 04构建游戏内键位重绑定设置界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-input-system安装后在 Claude Code 中输入/ue-input-system确认已加载。
附带文件references/input-action-reference.md
详细介绍
UE 输入系统
目标引擎:UE 5.8。下文中的 API 均已对照 5.8 头文件验证;较早版本的形式列在“已弃用 — 请勿使用”下。
Enhanced Input 是引擎唯一支持的输入路径。它位于 EnhancedInput 插件中(Engine/Plugins/EnhancedInput,在 5.8 中默认启用);请将 "EnhancedInput" 添加到 Build.cs 中的 PublicDependencyModuleNames,如果直接使用 FKey/EKeys,还需添加 "InputCore";如果使用输入模式筛选或用户设置失败原因容器,则需添加 "GameplayTags"。运行时组件包括 UEnhancedPlayerInput、UEnhancedInputComponent、UEnhancedInputLocalPlayerSubsystem、数据资产 UInputAction / UInputMappingContext,以及用于玩家按键重绑定的 UEnhancedInputUserSettings。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、启用的插件、GAS/网络设置)。如果该文件不存在,也不要停止。
根据请求和代码库确定相关领域。只有在两种合理理解会产生不同代码时才提问。
| 请求涉及…… | 请查看 |
|---|---|
| 插件、模块、ini 设置 | 设置 |
| 创建操作和映射上下文 | 输入资产 |
| 在 C++ 中绑定处理程序 | 在 C++ 中绑定操作 |
应绑定哪个事件(Started 与 Triggered 与 Completed) | 触发事件 |
| 按住、轻点、双击、脉冲、弦 | 内置触发器 |
| 死区、灵敏度、WASD→2D、反转 Y 轴 | 内置修改器 |
| 添加/移除上下文、优先级、分屏 | 映射上下文与优先级 |
| 按游戏状态启用/禁用上下文 | 输入模式筛选 |
| 玩家按键重绑定、设置界面、保存 | 运行时按键重绑定 |
| 没有 PlayerController 的 Actor 上的输入 | 没有 PlayerController 时的输入 |
| 按平台切换映射上下文 | 按平台配置输入数据 |
| 编写新的触发器或修改器 | 自定义触发器、自定义修改器 |
| 菜单、光标、CommonUI | UI 输入模式 |
| 调试叠加层 | 调试 |
各类的完整参数表见 references/input-action-reference.md。
设置
// Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
DefaultInput.ini — 将引擎的默认类指向 Enhanced Input:
[/Script/Engine.InputSettings]
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
Enhanced Input 项目设置位于 UEnhancedInputDeveloperSettings(项目设置 → 引擎 → Enhanced Input),并写入 DefaultInput.ini 中的 [/Script/EnhancedInput.EnhancedInputDeveloperSettings]。会改变 C++ 行为的键包括:bEnableUserSettings、UserSettingsClass、DefaultPlayerMappableKeyProfileClass、InputSettingsSaveSlotName、bEnableWorldSubsystem、DefaultWorldInputClass、bEnableInputModeFiltering、DefaultMappingContextInputModeQuery、DefaultInputMode、bEnableDefaultMappingContexts、bSendTriggeredEventsWhenInputIsFlushed。
输入资产
UInputAction : UDataAsset — 每个逻辑玩家操作对应一个。会影响 C++ 行为的属性:
| 属性 | 默认值 | 含义 |
|---|---|---|
ValueType | EInputActionValueType::Boolean | Boolean、Axis1D(float)、Axis2D(FVector2D)、Axis3D(FVector) |
AccumulationBehavior | TakeHighestAbsoluteValue | 或为 Cumulative — 所有映射值相加,因此 W + S 会相互抵消 |
bConsumeInput | true | 阻止同一按键上优先级较低的 Enhanced Input 映射 |
bConsumesActionAndAxisMappings | false | 还会阻止这些按键上的旧版 Action/Axis 映射 |
bReserveAllMappings | false | 更高优先级的上下文不会覆盖这些映射 |
bTriggerWhenPaused | false | 游戏暂停时仍触发操作 |
Triggers / Modifiers | 空 | 操作级设置,在每个映射的设置之后应用 |
PlayerMappableKeySettings | nullptr | UPlayerMappableKeySettings — 使操作可重新绑定 |
UInputMappingContext : UDataAsset — 按键到操作的绑定,以集合形式添加和移除。
| 成员 | 说明 |
|---|---|
DefaultKeyMappings | FInputMappingContextMappingData;使用 GetMappings() 读取数组 |
MappingProfileOverrides | TMap<FString, FInputMappingContextMappingData> — 按键配置文件的覆盖设置 |
RegistrationTrackingMode | Untracked(默认)或 CountRegistrations |
InputModeFilterOptions | UseProjectDefaultQuery(默认)、UseCustomQuery、DoNotFilter |
MapKey(Action, Key) / UnmapKey(Action, Key) | 编辑器/配置界面辅助函数,不是运行时重新绑定 API |
HasMappingForInputAction(Action) | 搜索默认映射和配置文件覆盖映射 |
每个 FEnhancedActionKeyMapping 都包含 Action、Key、映射级的 Triggers 和 Modifiers、SettingBehavior(EPlayerMappableKeySettingBehaviors:InheritSettingsFromAction、OverrideSettings、IgnoreSettings),以及可选的 PlayerMappableKeySettings 覆盖设置。可通过 GetPlayerMappableKeySettings() 和 GetMappingName() 访问。
在 C++ 中绑定操作
// MyCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
class UInputAction;
class UInputMappingContext;
struct FInputActionValue;
UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
protected:
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();
};
// MyCharacter.cpp
#include "MyCharacter.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "Engine/LocalPlayer.h"
#include "GameFramework/PlayerController.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();
APlayerController* PC = Cast<APlayerController>(GetController());
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystemFromController<UEnhancedInputLocalPlayerSubsystem>(PC))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
void AMyCharacter::Move(const FInputActionValue& Value)
{
const FVector2D Axis = Value.Get<FVector2D>();
AddMovementInput(GetActorForwardVector(), Axis.Y);
AddMovementInput(GetActorRightVector(), Axis.X);
}
void AMyCharacter::StartJump() { Jump(); }
void AMyCharacter::StopJump() { StopJumping(); }
处理函数签名
UEnhancedInputComponent::BindAction 返回 FEnhancedActionEventBinding&,并接受与三种原生委托签名之一匹配的成员函数,或按名称指定的 UFUNCTION:
| 签名 | 处理函数 |
|---|---|
FEnhancedInputActionHandlerSignature | void Handler() |
FEnhancedInputActionHandlerValueSignature | void Handler(const FInputActionValue& Value) |
FEnhancedInputActionHandlerInstanceSignature | void Handler(const FInputActionInstance& Instance) |
FEnhancedInputActionHandlerDynamicSignature | BindAction(Action, Event, Object, FName("OnFirePressed")) |
FInputActionInstance 提供 GetValue()、GetTriggerEvent()、GetElapsedTime()(Started + Ongoing + Triggered)、GetTriggeredTime()(仅 Triggered)、GetLastTriggeredWorldTime()、GetTriggers()、GetModifiers() 和 GetSourceAction()。
Lambda 和可轮询值:
EIC->BindActionValueLambda(InteractAction, ETriggerEvent::Triggered,
[this](const FInputActionValue& Value) { TryInteract(Value.Get<bool>()); });
EIC->BindActionInstanceLambda(ChargeAction, ETriggerEvent::Ongoing,
[this](const FInputActionInstance& Instance) { SetChargeProgress(Instance.GetElapsedTime()); });
// No delegate — just reflects the current value, poll it from Tick
FEnhancedInputActionValueBinding& LookBinding = EIC->BindActionValue(LookAction);
const FVector2D LookAxis = LookBinding.GetValue().Get<FVector2D>();
移除绑定:
FEnhancedInputActionEventBinding& Binding =
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &AMyCharacter::StartJump);
const uint32 BindingHandle = Binding.GetHandle(); // uint32, EnhancedInputComponent.h:165
EIC->RemoveBindingByHandle(BindingHandle); // one binding, later
EIC->ClearBindingsForObject(this); // every binding owned by this object; ClearActionBindings() drops all
触发事件
ETriggerEvent 是一个位掩码(ENUM_CLASS_FLAGS),用于描述本帧中观察到的触发状态转换。
| 事件 | 位 | 状态转换 | 适用场景 |
|---|---|---|---|
Triggered | 1 << 0 | None→Triggered、Ongoing→Triggered、Triggered→Triggered | 每个处于激活状态的帧;连续移动 |
Started | 1 << 1 | None→Ongoing、None→Triggered | 首次评估的帧;按下一次即触发的操作 |
Ongoing | 1 << 2 | Ongoing→Ongoing | 按住但尚未触发(蓄力累积) |
Canceled | 1 << 3 | Ongoing→None | 在满足触发条件前松开 |
Completed | 1 << 4 | Triggered→None | 触发后松开;停止连续操作 |
如果 Started 和 Triggered 在同一帧发生,Started 总会先触发。如果该操作上的任何触发器在某一帧报告 Ongoing,则该帧不会触发 Completed — 如果需要可靠地分别处理按下和松开,请拆分为两个操作。
内置触发器
各类的属性和默认值:references/input-action-reference.md。
| 类 | 显示名称 | 行为 |
|---|---|---|
UInputTriggerDown | 按下 | 值处于触发状态时,每帧触发(操作没有触发器时的隐式默认值) |
UInputTriggerPressed | 按下 | 首次触发时触发一次;持续按住不会重复触发 |
UInputTriggerReleased | 释放 | 触发后,值降至 ActuationThreshold 以下时触发一次 |
UInputTriggerHold | 持续按住 | 经过 HoldTimeThreshold 秒后触发;bIsOneShot = false 时会持续触发 |
UInputTriggerHoldAndRelease | 持续按住并释放 | 释放时,如果持续按住的时间至少达到 HoldTimeThreshold,则触发 |
UInputTriggerTap | 轻点 | 在 TapReleaseTimeThreshold 时间内释放 |
UInputTriggerRepeatedTap | 连续轻点 | 轻点 NumberOfTapsWhichTriggerRepeat 次,且每次间隔小于 RepeatDelay |
UInputTriggerPulse | 脉冲 | 按住期间每隔 Interval 秒触发;TriggerLimit 限制触发次数 |
UInputTriggerChordAction | 和弦操作 | 仅在 ChordAction 被触发时生效;会向同一按键的低优先级映射中注入一个 UInputTriggerChordBlocker |
ETriggerType 决定同一操作上的触发器如何组合:Explicit(至少一个触发器必须触发)、Implicit(所有触发器都必须触发)、Blocker(当 IsBlocking 返回 true 时阻止所有操作)。ETriggerEventsSupported(None、Instant、Uninterruptible、Ongoing、All)声明触发器可能产生哪些 ETriggerEvent。
内置修改器
先运行每个映射上的修改器,再运行操作级的 Modifiers 数组;每组均按数组顺序运行。
| 类 | 显示名称 | 效果 |
|---|---|---|
UInputModifierDeadZone | 死区 | 低于 LowerThreshold 时归零,在 UpperThreshold 处重新映射为 1;Type = Axial、Radial、UnscaledRadial |
UInputModifierScalar | 标量 | 按轴乘以 FVector Scalar |
UInputModifierScaleByDeltaTime | 按 Delta Time 缩放 | 乘以帧 DeltaTime |
UInputModifierNegate | 取反 | 对 bX / bY / bZ 选定的轴取反 |
UInputModifierSwizzleAxis | 变换输入轴值顺序 | 重新排列轴;Order 默认为 EInputAxisSwizzle::YXZ |
UInputModifierSmooth | 平滑 | 对近期样本计算滚动平均值 |
UInputModifierSmoothDelta | 平滑增量 | 平滑后的归一化增量;SmoothingMethod、Speed、EasingExponent |
UInputModifierResponseCurveExponential | 响应曲线 - 指数 | 对每个轴应用 sign(x) * pow(abs(x), CurveExponent) |
UInputModifierResponseCurveUser | 响应曲线 - 用户定义 | 每个轴使用一个 UCurveFloat |
UInputModifierFOVScaling | FOV 缩放 | 根据摄像机 FOV 缩放,以在变焦时保持恒定角速度 |
UInputModifierToWorldSpace | 转换为世界空间 | 2D 轴 → 世界空间 |
WASD → Axis2D(操作上的 AccumulationBehavior = Cumulative):
W:Swizzle Input Axis Values (YXZ)→ Y = +1S:Swizzle Input Axis Values (YXZ)+Negate (bY)→ Y = −1D:无 → X = +1A:Negate (bX)→ X = −1
游戏手柄摇杆:在每个摇杆映射上使用 Dead Zone (Radial, LowerThreshold = 0.2)。
鼠标视角:在映射上依次使用 Scalar(0.4, 0.4, 1.0)、Smooth 和 FOV Scaling。
映射上下文与优先级
Subsystem->AddMappingContext(GameplayContext, 0); // higher integer = higher priority
Subsystem->AddMappingContext(VehicleContext, 1);
FModifyContextOptions Options;
Options.bForceImmediately = true; // rebuild now instead of at end of frame
Options.bNotifyUserSettings = true; // register these mappings with UEnhancedInputUserSettings
Subsystem->AddMappingContext(MenuContext, 2, Options);
Subsystem->RemoveMappingContext(VehicleContext);
Subsystem->ClearAllMappings();
int32 FoundPriority = 0;
const bool bActive = Subsystem->HasMappingContext(GameplayContext, FoundPriority);
const FInputActionValue Current = Subsystem->GetPlayerInput()->GetActionValue(MoveAction);
FModifyContextOptions 的默认值:bIgnoreAllPressedKeysUntilRelease = true、bForceImmediately = false、bNotifyUserSettings = false。第一个选项已经会抑制上下文切换期间持续按住的按键所产生的幽灵输入——不要误以为它是可选项而显式设置;只有在你确实希望按住的按键在新上下文中继续生效时,才将其设为 false。
UInputAction 上的 bConsumeInput = true(默认值)表示:如果高优先级上下文映射了同一个物理按键,该按键的所有低优先级绑定都会被屏蔽。使用优先级和 bConsumeInput 隔离不同模式——车辆上下文若消费了空格键,驾驶时角色的跳跃动作就不会触发。
IMC 上的 RegistrationTrackingMode:Untracked(无论添加次数如何,第一次 RemoveMappingContext 都会生效)或 CountRegistrations(必须移除与添加次数相同的次数才会停止应用——多个系统共用一个 IMC 时请使用此模式)。
每个本地玩家都拥有独立的 UEnhancedInputLocalPlayerSubsystem,因此分屏上下文不会在玩家之间泄漏。使用 ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LocalPlayer) 或 ULocalPlayer::GetSubsystemFromController<UEnhancedInputLocalPlayerSubsystem>(PlayerController) 获取特定玩家的子系统。使用 InjectInputForAction(Action, RawValue, Modifiers, Triggers) 或 InjectInputVectorForAction(Action, Value, Modifiers, Triggers),无需设备即可模拟输入。
输入模式筛选
根据游戏标签状态启用或禁用整个映射上下文,而不是添加和移除上下文。需要在 UEnhancedInputDeveloperSettings 上启用 bEnableInputModeFiltering。
- 子系统保存当前模式:
GetInputMode()、SetInputMode(Tags, Options)、AppendTagsToInputMode、AddTagToInputMode、RemoveTagsFromInputMode、RemoveTagFromInputMode——这些方法均接受可选的FModifyContextOptions。 - 每个
UInputMappingContext通过InputModeFilterOptions选择匹配方式:UseProjectDefaultQuery(项目的DefaultMappingContextInputModeQuery)、UseCustomQuery(IMC 自身的InputModeQueryOverride)或DoNotFilter。 UEnhancedInputDeveloperSettings::DefaultInputMode会为每个新的UEnhancedPlayerInput设置初始模式。- 查询未通过的上下文仍会保持注册状态,但不再产生输入,因此优先级和注册计数不受影响。
Subsystem->SetInputMode(FGameplayTagContainer(MyGameplayTags::TAG_InputMode_Menu));
运行时按键重新绑定
UEnhancedInputUserSettings(UserSettings/EnhancedInputUserSettings.h)是官方指定的重新绑定途径。它是一个 USaveGame;启用 bEnableUserSettings 后,每个子系统都会创建一个实例,并保存到由 InputSettingsSaveSlotName 指定的槽位。
#include "EnhancedInputSubsystems.h"
#include "UserSettings/EnhancedInputUserSettings.h"
void UMyRebindWidget::RebindKey(FName MappingName, const FKey& NewKey)
{
UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystemFromController<UEnhancedInputLocalPlayerSubsystem>(GetOwningPlayer());
UEnhancedInputUserSettings* Settings = Subsystem ? Subsystem->GetUserSettings() : nullptr;
if (!Settings)
{
return;
}
FMapPlayerKeyArgs Args;
Args.MappingName = MappingName;
Args.Slot = EPlayerMappableKeySlot::First;
Args.NewKey = NewKey;
FGameplayTagContainer FailureReason;
Settings->MapPlayerKey(Args, FailureReason);
if (FailureReason.IsEmpty())
{
Settings->SaveSettings();
}
}
使用 RegisterInputMappingContext(IMC) 注册设置界面必须列出的任何 IMC,即使它从未添加到子系统中也不例外;添加上下文时设置 bNotifyUserSettings = true 也会自动完成注册。使用 FindMappingsInRow(MappingName) 和 FindCurrentMappingForSlot(MappingName, Slot) 读取映射,使用 ResetAllPlayerKeysInRow 或 ResetKeyProfileIdToDefault 重置,并通过 SaveSettings() / AsyncSaveSettings() 保存。配置文件标识符的类型是 FString,而非 FGameplayTag——ProfileIdString、GetProfileIdString()、GetActiveKeyProfileId()、GetKeyProfileWithId()、SetActiveKeyProfile()。
动作或映射只有在携带 UPlayerMappableKeySettings(Name、DisplayName、DisplayCategory、Metadata、SupportedKeyProfileIds)时才能重新绑定——它可以位于 UInputAction 上,也可以位于 FEnhancedActionKeyMapping 上,并将 SettingBehavior = OverrideSettings。完整 API 和结构体表格见:references/input-action-reference.md。
没有 PlayerController 时的输入
UEnhancedInputWorldSubsystem(在 5.8 中为实验性功能)让永远不会被控制的 Actor 也能接收输入委托。在 UEnhancedInputDeveloperSettings 中启用 bEnableWorldSubsystem 即可。
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/World.h"
void AMyDoor::BeginPlay()
{
Super::BeginPlay();
// AddActorInputComponent reads AActor::InputComponent, created only for possessed actors.
if (!InputComponent)
{
InputComponent = NewObject<UEnhancedInputComponent>(this, TEXT("DoorInputComponent"));
InputComponent->RegisterComponent();
}
UEnhancedInputComponent* EIC = CastChecked<UEnhancedInputComponent>(InputComponent);
EIC->BindAction(OpenAction, ETriggerEvent::Started, this, &AMyDoor::Open);
if (UEnhancedInputWorldSubsystem* WorldInput =
GetWorld()->GetSubsystem<UEnhancedInputWorldSubsystem>())
{
WorldInput->AddActorInputComponent(this);
WorldInput->AddMappingContext(DoorContext, 0);
}
}
每次调用 AddActorInputComponent,都要在 EndPlay 时配对调用 RemoveActorInputComponent。该子系统实现了与本地玩家子系统相同的 IEnhancedInputSubsystemInterface,因此 AddMappingContext、InjectInputForAction 等方法的行为完全相同,但它没有用户设置,也没有本地玩家。
每个平台的输入数据
UEnhancedInputPlatformSettings(UPlatformSettings,UEnhancedInputPlatformSettings::Get())包含一个 UEnhancedInputPlatformData 子类数组 InputData。每个子类都定义了 MappingContextRedirects,这是一个 TMap,用于在相应平台上将一个 UInputMappingContext 替换为另一个;GetContextRedirect(Context) 会返回替换后的上下文,或原始上下文。借此可以替换为触屏或主机上下文,而无需在游戏逻辑代码中添加分支。在 Build.cs 中调用 Get() 需要添加 DeveloperSettings(它会内联调用 UPlatformSettingsManager)。
自定义触发器
继承 UInputTrigger 并重写 UpdateState_Implementation,返回 ETriggerState::None、Ongoing 或 Triggered。
// MyDoubleClickTrigger.h
#pragma once
#include "EnhancedPlayerInput.h"
#include "Engine/World.h"
#include "InputTriggers.h"
#include "MyDoubleClickTrigger.generated.h"
UCLASS(EditInlineNew, meta = (DisplayName = "Double Click"))
class MYGAME_API UMyDoubleClickTrigger : 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
{
const bool bActuated = IsActuated(ModifiedValue);
const float Now = PlayerInput->GetWorld()->GetTimeSeconds();
ETriggerState Result = ETriggerState::None;
if (bActuated && !bWasActuated)
{
if ((Now - LastPressTime) <= DoubleClickThreshold)
{
LastPressTime = 0.f;
Result = ETriggerState::Triggered;
}
else
{
LastPressTime = Now;
}
}
bWasActuated = bActuated;
return Result;
}
private:
float LastPressTime = 0.f;
bool bWasActuated = false;
};
IsActuated(Value) 的判断条件为 Value.GetMagnitudeSq() >= ActuationThreshold * ActuationThreshold。对于基于时间的触发器,请继承 UInputTriggerTimedBase:它会在激活时转为 Ongoing,并提供 HeldDuration、CalculateHeldDuration(PlayerInput, DeltaTime) 和 bAffectedByTimeDilation。重写 IsBlocking(const ETriggerState State) const 可实现阻挡器,重写 GetDebugState() const 可供调试 HUD 使用。如果触发器会在映射重建后保留状态,请重写 ReceiveTriggerReinstanced_Implementation(const UInputTrigger* OldTrigger) 并调用 Super。
自定义修饰器
继承 UInputModifier 并重写:
virtual FInputActionValue ModifyRaw_Implementation(
const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override;
返回值会在进一步处理前转换回操作的 ValueType,因此内部可以返回任意形状的值——FInputActionValue(CurrentValue.GetValueType(), Vector) 会保留该类型。重写 GetVisualizationColor_Implementation(FInputActionValue SampleValue, FInputActionValue FinalValue) const 可控制调试叠加层的颜色。完整示例:references/input-action-reference.md。
UI 输入模式
不使用 CommonUI 时,可通过 SetInputMode(FInputModeUIOnly())、SetInputMode(FInputModeGameAndUI()) 或 SetInputMode(FInputModeGameOnly()) 在 PlayerController 上切换模式。
CommonUI 通过 UCommonActivatableWidget 堆栈路由输入,并省去了大多数手动调用 SetInputMode 的操作。Enhanced Input 与 Common Input 已统一:启用 UCommonInputSettings::bEnableEnhancedInputSupport(Project Settings → Game → Common Input;需要重启)后,CommonUI 的点击和返回操作会变为 UInputAction 资源——UCommonUIInputData::EnhancedInputClickAction 和 EnhancedInputBackAction——而不是数据表行。Widget 堆栈、可激活 Widget 和输入配置属于 ue-ui-umg-slate。
调试
ShowDebug EnhancedInput 会输出已应用的映射上下文、每个操作的值和触发器状态,以及每个触发器的 GetDebugState()。ShowDebug WorldSubsystemInput 会输出 UEnhancedInputWorldSubsystem 的相同信息;ShowDebug InputSettings 会转储开发者设置;将 LogWorldSubsystemInput 设为 VeryVerbose 时,会记录世界子系统处理的每个按键。
已弃用 — 请勿使用
| 不要使用 | 在 5.8 中使用 | 来源 |
|---|---|---|
UInputTriggerCombo | 无替代方案 — 使用 UInputTriggerChordAction、计时器或状态机自行构建序列 | InputTriggers.h:571 中的 UE_DEPRECATED(5.8) |
FInputComboStepData、FInputCancelAction | 无替代方案 | InputTriggers.h:530,551 中的 UE_DEPRECATED(5.8) |
FEnhancedInputKeys::ComboKey | 无替代方案 | EnhancedInputModule.h:20 中的 UE_DEPRECATED(5.8) |
UInputMappingContext::Mappings | DefaultKeyMappings / GetMappings() | InputMappingContext.h:93 中的 UE_DEPRECATED(5.7) |
UEnhancedInputPlatformSettings::LoadInputDataClasses()、InputDataClasses | GetInputData() / ForEachInputData() | EnhancedInputPlatformSettings.h:77,88 中的 UE_DEPRECATED(5.7) |
UEnhancedPlayerInput::GetAppliedInputContexts() | GetAppliedInputContextData() | EnhancedPlayerInput.h:175 中的 UE_DEPRECATED(5.6) |
FMapPlayerKeyArgs::ProfileId(FGameplayTag)、SetKeyProfile(FGameplayTag)、GetCurrentKeyProfile()、GetCurrentKeyProfileIdentifier()、GetAllSavedKeyProfiles()、GetKeyProfileWithIdentifier()、ResetKeyProfileToDefault(FGameplayTag)、GetProfileIdentifer() | ProfileIdString(FString)、SetActiveKeyProfile(FString)、GetActiveKeyProfile()、GetActiveKeyProfileId()、GetAllAvailableKeyProfiles()、GetKeyProfileWithId()、ResetKeyProfileIdToDefault(FString)、GetProfileIdString() | UserSettings/EnhancedInputUserSettings.h:86,706,730,722,754,776,673,327 中的 UE_DEPRECATED(5.6) |
UPlayerMappableKeySettings::SupportedKeyProfiles | SupportedKeyProfileIds(TArray<FString>) | PlayerMappableKeySettings.h:62 中的 UE_DEPRECATED(5.6) |
UEnhancedInputDeveloperSettings::bShouldLogAllWorldSubsystemInputs | 在 VeryVerbose 级别记录 LogWorldSubsystemInput | EnhancedInputDeveloperSettings.h:150 中的 UE_DEPRECATED(5.6) |
UPlayerMappableInputConfig | UEnhancedInputUserSettings | PlayerMappableInputConfig.h:23 中的 UE_DEPRECATED(5.3) |
UInputComponent::BindAxis、BindAction(FName, EInputEvent, Object, Func)、BindAxisKey、BindVectorAxis、BindKey、BindTouch、BindGesture | UEnhancedInputComponent::BindAction(UInputAction*, ETriggerEvent, Object, Func) | EnhancedInputComponent.h:613-634 中的 UEnhancedInputComponent 上对应函数被标记为 = delete |
常见错误
**在 SetupPlayerInputComponent 中添加映射上下文:**该调用可能在控制器和本地玩家解析完成前运行。请在 BeginPlay、OnPossess/PossessedBy 中添加上下文,或通过 PlayerControllerChanged 处理程序添加。
**将 bIgnoreAllPressedKeysUntilRelease = true 设为“以防万一”:**它已经是 FModifyContextOptions 的默认值。只有要将其设为 false 时才需要修改。
为按钮按下绑定 Triggered:Triggered 会在每个有效帧触发。按下时触发一次请使用 Started,释放时请使用 Completed。
**期待 Ongoing 同时触发 Completed:**只要某个触发器在任意一帧报告 Ongoing,Completed 就会被抑制。请拆分为两个各自带有触发器的动作。
**摇杆没有死区:**模拟摇杆静止时的值也不为零。请为每个摇杆映射添加 Dead Zone (Radial)。
**WASD 缺少轴值重映射:**键盘按键会在 X 轴上产生 1D 值。若不为 W 和 S 设置 Swizzle Input Axis Values (YXZ),前进/后退值就无法传递到 Axis2D 动作的 Y 分量。
**为玩家重新绑定按键时调用 MapKey/UnmapKey:**这些函数会修改 IMC 资源,是编辑器/配置辅助函数。请改用 UEnhancedInputUserSettings::MapPlayerKey,并在添加需要显示在重新绑定界面中的上下文时传入 bNotifyUserSettings = true。
**复制触发器事件:**输入仅在客户端本地处理。请复制结果(如移动输入、能力激活),不要复制 ETriggerEvent。
在 UEnhancedInputComponent 上使用旧式绑定:BindAxis 和 FName 版 BindAction 重载已标记为 = delete,会导致编译失败。请迁移这些绑定,而不要定义 ENHANCED_INPUT_ALLOW_LEGACY_BINDING=1。
相关技能
ue-gameplay-framework—APlayerController、控制权交接、SetupPlayerInputComponent生命周期ue-ui-umg-slate— CommonUI 可激活窗口小部件堆栈、输入配置、光标和焦点ue-character-movement— 通过AddMovementInput使用FInputActionValueue-gameplay-abilities— 从输入激活能力、UAbilitySystemComponent输入 IDue-actor-component-architecture—UActorComponent/UInputComponent所有权、EnableInput、组件拥有的输入- 另请参阅:
ue-cpp-foundations、ue-game-features、ue-gameplay-cameras、ue-mover