--- title: Untitled date: 2025-08-27 17:03:13 excerpt: tags: rating: ⭐ --- # 前言 - 官方文档 - [Enhanced Input](https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine?application_version=5.5) - [Using CommonUI With Enhanced Input](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-commonui-with-enhnaced-input-in-unreal-engine?application_version=5.5) - 知乎文章 - [UE5 -- Lyra中的输入模块(Input)](https://zhuanlan.zhihu.com/p/537949870) - [UE5 Lyra的多模态输入和修改灵敏度配置方案](https://zhuanlan.zhihu.com/p/30566880169) ## 调试命令 - showdebug enhancedinput:会显示你的项目的可用输入动作和轴映射。 - showdebug devices # 相关类 - **Input Actions**:配置输入动作 => 数据或者状态用的资产。 - **Input Mapping Contexts**:输入Action的集合。可以为不同GameMode配置不同的 **Input Mapping Contexts**来满足自定义输入的需求。 - **Input Modifiers**: 是一种预处理器,能够修改UE接收的原始输入值,然后再将其发送给输入触发器(Input Trigger)。增强输入插件随附多种输入修饰器,可以执行各种任务,例如更改轴顺序、实现"死区"、将轴输入转换为世界空间。 - **Input Triggers**:用于确定用户输入在经过一系列可选输入修饰器的处理后,是否会激活输入映射上下文中的相应输入动作。 - 多平台相关:位于Project Settings -> Enhanced Input -> Platform Settings -> Input Data - Mapping Context Redirect:将不同的输入设置用于不同的平台。 - Enhanced Input Platform Data:为你的游戏添加特定于平台的选项。 **InputAction**绑定: ```c++ void AFooBar::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { UEnhancedInputComponent* Input = Cast(PlayerInputComponent); // 你可以通过更改"ETriggerEvent"枚举值,绑定到此处的任意触发器事件 Input->BindAction(AimingInputAction, ETriggerEvent::Triggered, this, &AFooBar::SomeCallbackFunc); } void AFooBar::SomeCallbackFunc(const FInputActionInstance& Instance) { // 获取此处所需任意类型的输入动作的值... FVector VectorValue = Instance.GetValue().Get(); FVector2D 2DAxisValue = Instance.GetValue().Get(); float FloatValue = Instance.GetValue().Get(); bool BoolValue = Instance.GetValue().Get(); // 在此处实现你的精彩功能! } ``` Runtime **InputMappingContext**添加: ```c++ // 将映射上下文公开为头文件中的属性... UPROPERTY(EditAnywhere, Category="Input") TSoftObjectPtr InputMapping; // 在你的cpp中... if (ULocalPlayer* LocalPlayer = Cast(Player)) { if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem()) { if (!InputMapping.IsNull()) { InputSystem->AddMappingContext(InputMapping.LoadSynchronous(), Priority); } } } ``` **Input Trigger Timed Base** 会检查输入是否已被按住一段时间,如是,则接受该输入并返回 **持续(Ongoing)** 状态。 ```c++ /** UInputTriggerHold 触发器会在输入保持激活状态达到HoldTimeThreshold秒之后触发。 触发器可以选择触发一次或反复触发。 */ UCLASS(NotBlueprintable, MinimalAPI, meta = (DisplayName = "Hold")) class UInputTriggerHold final : public UInputTriggerTimedBase { GENERATED_BODY() bool bTriggered = false; protected: virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override; public: virtual ETriggerEventsSupported GetSupportedTriggerEvents() const override { return ETriggerEventsSupported::Ongoing; } // 输入要保持多久才能导致触发? UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings", meta = (ClampMin = "0")) float HoldTimeThreshold = 1.0f; // 此触发器应该仅触发一次,还是在满足保持时间阈值之后每帧触发? UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") bool bIsOneShot = false; virtual FString GetDebugState() const override { return HeldDuration ? FString::Printf(TEXT("Hold:%.2f/%.2f"), HeldDuration, HoldTimeThreshold) : FString(); } }; ETriggerState UInputTriggerHold::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) { // 更新HeldDuration并派生基础状态 ETriggerState State = Super::UpdateState_Implementation(PlayerInput, ModifiedValue, DeltaTime); // 在HeldDuration达到阈值时触发 bool bIsFirstTrigger = !bTriggered; bTriggered = HeldDuration >= HoldTimeThreshold; if (bTriggered) { return (bIsFirstTrigger || !bIsOneShot) ? ETriggerState::Triggered : ETriggerState::None; } return State; } ``` # 按键设置 # Lyra中的相关实现 ## ULyraHeroComponent中的输入绑定 ```c++ void ULyraHeroComponent::InitializePlayerInput(UInputComponent* PlayerInputComponent) { check(PlayerInputComponent); const APawn* Pawn = GetPawn(); if (!Pawn) { return; } const APlayerController* PC = GetController(); check(PC); const ULyraLocalPlayer* LP = Cast(PC->GetLocalPlayer()); check(LP); UEnhancedInputLocalPlayerSubsystem* Subsystem = LP->GetSubsystem(); check(Subsystem); Subsystem->ClearAllMappings(); if (const ULyraPawnExtensionComponent* PawnExtComp = ULyraPawnExtensionComponent::FindPawnExtensionComponent(Pawn)) { if (const ULyraPawnData* PawnData = PawnExtComp->GetPawnData()) { //获取ULyraInputConfig DataAesset,存储TArray,FLyraInputAction里为InputAction、GameplayTag。 if (const ULyraInputConfig* InputConfig = PawnData->InputConfig) { for (const FInputMappingContextAndPriority& Mapping : DefaultInputMappings) { if (UInputMappingContext* IMC = Mapping.InputMapping.Get()) { if (Mapping.bRegisterWithSettings) { //将IMC注册给UEnhancedInputUserSettings if (UEnhancedInputUserSettings* Settings = Subsystem->GetUserSettings()) { Settings->RegisterInputMappingContext(IMC); } FModifyContextOptions Options = {}; Options.bIgnoreAllPressedKeysUntilRelease = false; // Actually add the config to the local player //实际添加IMC给LocalPlayer Subsystem->AddMappingContext(IMC, Mapping.Priority, Options); } } } // The Lyra Input Component has some additional functions to map Gameplay Tags to an Input Action. // If you want this functionality but still want to change your input component class, make it a subclass // of the ULyraInputComponent or modify this component accordingly. ULyraInputComponent* LyraIC = Cast(PlayerInputComponent); if (ensureMsgf(LyraIC, TEXT("Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to ULyraInputComponent or a subclass of it."))) { // Add the key mappings that may have been set by the player LyraIC->AddInputMappings(InputConfig, Subsystem); // This is where we actually bind and input action to a gameplay tag, which means that Gameplay Ability Blueprints will // be triggered directly by these input actions Triggered events. TArray BindHandles; LyraIC->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles); LyraIC->BindNativeAction(InputConfig, LyraGameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, LyraGameplayTags::InputTag_Look_Mouse, ETriggerEvent::Triggered, this, &ThisClass::Input_LookMouse, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, LyraGameplayTags::InputTag_Look_Stick, ETriggerEvent::Triggered, this, &ThisClass::Input_LookStick, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, LyraGameplayTags::InputTag_Crouch, ETriggerEvent::Triggered, this, &ThisClass::Input_Crouch, /*bLogIfNotFound=*/ false); LyraIC->BindNativeAction(InputConfig, LyraGameplayTags::InputTag_AutoRun, ETriggerEvent::Triggered, this, &ThisClass::Input_AutoRun, /*bLogIfNotFound=*/ false); } } } } if (ensure(!bReadyToBindInputs)) { bReadyToBindInputs = true; } UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast(PC), NAME_BindInputsNow); UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(const_cast(Pawn), NAME_BindInputsNow); } ``` # 按键设置与保存 - [Player Mappable Keys using Enhanced Input](https://dev.epicgames.com/community/learning/tutorials/Vp69/unreal-engine-player-mappable-keys-using-enhanced-input) ## 相关类 - **UEnhancedInputUserSettings** (~~PlayerMappableInputConfig~~): 用户输入设置类。通过EnhancedInputLocalPlayerSubsystem->GetEnhancedInputUserSettings()获取。 - UEnhancedPlayerMappableKeyProfile:一个用户当前设置的实例。比如“默认”与“左撇子"。 ## 步骤 1. 假设已经配置好了IMC以及IA。 1. 在Controller/Character中注册IMC与IA? 2. AddMappingContext()的选项OptionsNotifyUserSettings设置为true,会调用UEnhancedInputUserSettings::RegisterInputMappingContext() 2. ProjectSettings - Engine - EnhancedInput - UserSettings中的Enable User Settings 3. 配置IA与IMC中的UserSetting选项。 1. 部分输入比如Axis2D类 ## 加载按键设置逻辑 默认自动加载,载入逻辑位于UEnhancedInputUserSettings::LoadOrCreateSettings(),调用顺序是: UEnhancedInputLocalPlayerSubsystem::Initialize -> UEnhancedInputLocalPlayerSubsystem::InitalizeUserSettings() -> UEnhancedInputUserSettings::LoadOrCreateSettings() ```c++ UEnhancedInputUserSettings* UEnhancedInputUserSettings::LoadOrCreateSettings(ULocalPlayer* LocalPlayer) { UEnhancedInputUserSettings* Settings = nullptr; if (!LocalPlayer) { UE_LOG(LogEnhancedInput, Log, TEXT("Unable to determine an owning Local Player for the given Enhanced Player Input object")); return nullptr; } // If the save game exists, load it. if (UGameplayStatics::DoesSaveGameExist(UE::EnhancedInput::SETTINGS_SLOT_NAME, LocalPlayer->GetLocalPlayerIndex())) { USaveGame* Slot = UGameplayStatics::LoadGameFromSlot(UE::EnhancedInput::SETTINGS_SLOT_NAME, LocalPlayer->GetLocalPlayerIndex()); Settings = Cast(Slot); } // If there is no settings save game object, then we can create on // based on the class type set in the developer settings const UEnhancedInputDeveloperSettings* DevSettings = GetDefault(); UClass* SettingsClass = DevSettings->UserSettingsClass ? DevSettings->UserSettingsClass.Get() : UEnhancedInputUserSettings::StaticClass(); // This property is marked as "NoClear", so this should be impossible. if (!ensureMsgf(SettingsClass, TEXT("Invalid Enhanced Input User settings class!"))) { return nullptr; } UE_CLOG((Settings && (Settings->GetClass() != SettingsClass)), LogEnhancedInput, Log, TEXT("[%hs] Enhanced Input User Settings class has been changed from '%s' to '%s'. A new settings save object will be created (your saved settings will be reset)"), __func__, *GetNameSafe(Settings->GetClass()), *GetNameSafe(SettingsClass)); // If the settings are null (they dont exist yet) or the class has changed, we need to create a new object. // The class can change if you modify it in the editor to be something else if (Settings == nullptr || (Settings->GetClass() != SettingsClass)) { Settings = Cast(UGameplayStatics::CreateSaveGameObject(SettingsClass)); } if (ensure(Settings)) { Settings->Initialize(LocalPlayer); Settings->ApplySettings(); } return Settings; } ```