К основному содержанию
скилл CX Codex

ue-input-system — скилл для Claude Code: ввод в Unreal C++

Скилл по Enhanced Input в Unreal Engine 5.8: привязка действий, контексты маппинга, триггеры, модификаторы и переназначение клавиш в рантайме.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add quodsoler/unreal-engine-skills --skill ue-input-system -a claude-code
Открыть на GitHub

Что делает ue-input-system

Скилл помогает агенту писать код ввода для Unreal C++ на Enhanced Input. Внутри — разбор UInputAction и UInputMappingContext, привязка обработчиков через SetupPlayerInputComponent, выбор события (Started, Triggered, Completed), встроенные триггеры вроде hold и double-tap, модификаторы для мёртвых зон и WASD в 2D. Отдельная таблица ведёт от запроса к нужному разделу: контексты и приоритеты, фильтрация по режиму игры, ввод без PlayerController, данные под конкретную платформу, отладка.

Перед работой в Build.cs нужно добавить "EnhancedInput" в PublicDependencyModuleNames, а в DefaultInput.ini прописать DefaultPlayerInputClass и DefaultInputComponentClass. Если код трогает FKey или EKeys, понадобится ещё "InputCore", а для фильтрации по режиму ввода и контейнеров причин отказа в пользовательских настройках — "GameplayTags". Без этих модулей сборка упадёт на линковке, так что это первое, что стоит проверить.

Скилл читает .agents/ue-project-context.md, если тот есть, и не останавливается, когда файла нет. API сверены с заголовками 5.8, устаревшие формы вынесены в отдельный раздел. Для ввода в UI и маршрутизации CommonUI скилл отправляет к ue-ui-umg-slate, а за владение PlayerController и Pawn — к ue-gameplay-framework.

Какие задачи решает

  • создать UInputAction и UInputMappingContext под действие игрока
  • привязать обработчик движения и прыжка в SetupPlayerInputComponent
  • выбрать между Started, Triggered и Completed для конкретного действия
  • настроить мёртвую зону и перевод WASD в двумерный вектор
  • сделать переназначение клавиш в рантайме через UEnhancedInputUserSettings

Примеры запросов агенту

  • Помоги настроить Enhanced Input для персонажа: контекст маппинга и действие на прыжок
  • Почему у меня не срабатывает Triggered при удержании — разбери триггеры
  • Сделай экран переназначения клавиш через UEnhancedInputUserSettings

Как установить ue-input-system

1Через Skills CLI (Vercel)

npx skills add quodsoler/unreal-engine-skills --skill ue-input-system -a claude-code

2Вручную: положить файлы

~/.claude/skills/ue-input-system/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/ue-input-system/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о ue-input-system

Как установить ue-input-system в Claude?

Выполните npx skills add quodsoler/unreal-engine-skills --skill ue-input-system -a claude-code. Скилл окажется в ~/.claude/skills/ue-input-system/SKILL.md.

Что делает ue-input-system?

Даёт агенту справочник по Enhanced Input в Unreal C++: ассеты действий, контексты маппинга, привязка обработчиков, триггеры, модификаторы и переназначение клавиш.

С какими скиллами он сочетается?

Ввод в UI и CommonUI разбирает ue-ui-umg-slate, а владение PlayerController и Pawn — ue-gameplay-framework.

Исходный текст

SKILL.md · MIT
---
name: ue-input-system
description: "Use when wiring player input in Unreal C++ with Enhanced Input — input actions, mapping contexts, triggers, modifiers, binding handlers and runtime key rebinding. Also use when the user mentions 'Enhanced Input', 'UInputAction', 'UInputMappingContext', 'AddMappingContext', 'BindAction', 'SetupPlayerInputComponent', 'ETriggerEvent', 'input trigger', 'input modifier', 'SwizzleAxis', 'dead zone', 'WASD', 'gamepad', 'key rebinding', 'UEnhancedInputUserSettings' or 'input mapping priority'. For UI input modes and CommonUI routing, see ue-ui-umg-slate; for PlayerController/Pawn possession, see ue-gameplay-framework."
metadata:
  version: "2.0.0"
  engine: "5.8"
---

# UE Input System

Target engine: **UE 5.8**. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".

Enhanced Input is the engine's only supported input path. It lives in the `EnhancedInput` plugin (`Engine/Plugins/EnhancedInput`, enabled by default in 5.8); add `"EnhancedInput"` to `PublicDependencyModuleNames` in your `Build.cs`, plus `"InputCore"` if you touch `FKey`/`EKeys` directly and `"GameplayTags"` if you use input-mode filtering or the user-settings failure-reason containers. The runtime pieces are `UEnhancedPlayerInput`, `UEnhancedInputComponent`, `UEnhancedInputLocalPlayerSubsystem`, the data assets `UInputAction` / `UInputMappingContext`, and `UEnhancedInputUserSettings` for player rebinding.

## Context

Read `.agents/ue-project-context.md` if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.

Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.

| Request is about… | Go to |
|---|---|
| Plugin, module, ini setup | [Setup](#setup) |
| Creating actions and mapping contexts | [Input Assets](#input-assets) |
| Binding handlers in C++ | [Binding Actions in C++](#binding-actions-in-c) |
| Which event to bind (`Started` vs `Triggered` vs `Completed`) | [Trigger Events](#trigger-events) |
| Hold, tap, double-tap, pulse, chord | [Built-in Triggers](#built-in-triggers) |
| Dead zones, sensitivity, WASD→2D, invert Y | [Built-in Modifiers](#built-in-modifiers) |
| Adding/removing contexts, priority, split-screen | [Mapping Contexts and Priority](#mapping-contexts-and-priority) |
| Enabling/disabling contexts by gameplay state | [Input Mode Filtering](#input-mode-filtering) |
| Player key rebinding, settings screens, saving | [Runtime Key Rebinding](#runtime-key-rebinding) |
| Input on actors with no PlayerController | [Input Without a PlayerController](#input-without-a-playercontroller) |
| Per-platform mapping context swaps | [Per-Platform Input Data](#per-platform-input-data) |
| Writing a new trigger or modifier | [Custom Triggers](#custom-triggers), [Custom Modifiers](#custom-modifiers) |
| Menus, cursors, CommonUI | [UI Input Mode](#ui-input-mode) |
| Debug overlays | [Debugging](#debugging) |

Full per-class parameter tables live in [references/input-action-reference.md](references/input-action-reference.md).

## Setup

```csharp
// Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
```

`DefaultInput.ini` — point the engine's default classes at Enhanced Input:
```ini
[/Script/Engine.InputSettings]
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
```

Enhanced Input project settings live on `UEnhancedInputDeveloperSettings` (Project Settings → Engine → Enhanced Input), written to `DefaultInput.ini` under `[/Script/EnhancedInput.EnhancedInputDeveloperSettings]`. Keys that change C++ behaviour: `bEnableUserSettings`, `UserSettingsClass`, `DefaultPlayerMappableKeyProfileClass`, `InputSettingsSaveSlotName`, `bEnableWorldSubsystem`, `DefaultWorldInputClass`, `bEnableInputModeFiltering`, `DefaultMappingContextInputModeQuery`, `DefaultInputMode`, `bEnableDefaultMappingContexts`, `bSendTriggeredEventsWhenInputIsFlushed`.

## Input Assets

`UInputAction : UDataAsset` — one per logical player action. Properties that change C++ behaviour:

| Property | Default | Meaning |
|---|---|---|
| `ValueType` | `EInputActionValueType::Boolean` | `Boolean`, `Axis1D` (float), `Axis2D` (`FVector2D`), `Axis3D` (`FVector`) |
| `AccumulationBehavior` | `TakeHighestAbsoluteValue` | Or `Cumulative` — all mappings sum, so W + S cancel |
| `bConsumeInput` | `true` | Blocks lower-priority Enhanced Input mappings on the same keys |
| `bConsumesActionAndAxisMappings` | `false` | Also blocks legacy Action/Axis mappings on those keys |
| `bReserveAllMappings` | `false` | Mappings are not overridden by higher-priority contexts |
| `bTriggerWhenPaused` | `false` | Action fires while the game is paused |
| `Triggers` / `Modifiers` | empty | Action-level, applied **after** the per-mapping ones |
| `PlayerMappableKeySettings` | `nullptr` | `UPlayerMappableKeySettings` — makes the action rebindable |

`UInputMappingContext : UDataAsset` — key-to-action bindings, added and removed as a set.

| Member | Notes |
|---|---|
| `DefaultKeyMappings` | `FInputMappingContextMappingData`; read the array with `GetMappings()` |
| `MappingProfileOverrides` | `TMap<FString, FInputMappingContextMappingData>` — per key-profile overrides |
| `RegistrationTrackingMode` | `Untracked` (default) or `CountRegistrations` |
| `InputModeFilterOptions` | `UseProjectDefaultQuery` (default), `UseCustomQuery`, `DoNotFilter` |
| `MapKey(Action, Key)` / `UnmapKey(Action, Key)` | Editor/config-screen helpers, **not** a runtime rebinding API |
| `HasMappingForInputAction(Action)` | Search default and profile-override mappings |

Each `FEnhancedActionKeyMapping` carries `Action`, `Key`, per-mapping `Triggers`, `Modifiers`, a `SettingBehavior` (`EPlayerMappableKeySettingBehaviors`: `InheritSettingsFromAction`, `OverrideSettings`, `IgnoreSettings`) and an optional `PlayerMappableKeySettings` override. Reach them with `GetPlayerMappableKeySettings()` and `GetMappingName()`.

## Binding Actions in C++

```cpp
// 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();
};
```

```cpp
// 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(); }
```

### Handler signatures

`UEnhancedInputComponent::BindAction` returns `FEnhancedInputActionEventBinding&` and accepts a member function matching one of three native delegate signatures, or a `UFUNCTION` by name:

| Signature | Handler |
|---|---|
| `FEnhancedInputActionHandlerSignature` | `void Handler()` |
| `FEnhancedInputActionHandlerValueSignature` | `void Handler(const FInputActionValue& Value)` |
| `FEnhancedInputActionHandlerInstanceSignature` | `void Handler(const FInputActionInstance& Instance)` |
| `FEnhancedInputActionHandlerDynamicSignature` | `BindAction(Action, Event, Object, FName("OnFirePressed"))` |

`FInputActionInstance` exposes `GetValue()`, `GetTriggerEvent()`, `GetElapsedTime()` (Started + Ongoing + Triggered), `GetTriggeredTime()` (Triggered only), `GetLastTriggeredWorldTime()`, `GetTriggers()`, `GetModifiers()` and `GetSourceAction()`.

Lambdas and pollable values:
```cpp
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>();
```

Removing bindings:
```cpp
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
```

## Trigger Events

`ETriggerEvent` is a bitmask (`ENUM_CLASS_FLAGS`) describing the trigger-state transitions seen this tick.

| Event | Bit | State transition | Bind it for |
|---|---|---|---|
| `Triggered` | `1 << 0` | None→Triggered, Ongoing→Triggered, Triggered→Triggered | Every active frame; continuous movement |
| `Started` | `1 << 1` | None→Ongoing, None→Triggered | First frame of evaluation; press-once actions |
| `Ongoing` | `1 << 2` | Ongoing→Ongoing | Held but not yet triggered (charge build-up) |
| `Canceled` | `1 << 3` | Ongoing→None | Released before the trigger condition was met |
| `Completed` | `1 << 4` | Triggered→None | Release after triggering; stop continuous actions |

`Started` always fires before `Triggered` when both occur on the same tick. `Completed` does **not** fire on a tick where any trigger on the action reports `Ongoing` — split press and release into two actions if you need both reliably.

## Built-in Triggers

Per-class properties and defaults: [references/input-action-reference.md](references/input-action-reference.md).

| Class | Display name | Behavior |
|---|---|---|
| `UInputTriggerDown` | Down | Fires every frame the value is actuated (implicit default when an action has no triggers) |
| `UInputTriggerPressed` | Pressed | Once on first actuation; holding does not repeat |
| `UInputTriggerReleased` | Released | Once when the value drops below `ActuationThreshold` after actuation |
| `UInputTriggerHold` | Hold | After `HoldTimeThreshold` seconds; `bIsOneShot = false` keeps firing |
| `UInputTriggerHoldAndRelease` | Hold And Release | On release, if held at least `HoldTimeThreshold` |
| `UInputTriggerTap` | Tap | Released within `TapReleaseTimeThreshold` |
| `UInputTriggerRepeatedTap` | Repeated Tap | `NumberOfTapsWhichTriggerRepeat` taps, each gap under `RepeatDelay` |
| `UInputTriggerPulse` | Pulse | Every `Interval` seconds while held; `TriggerLimit` caps the count |
| `UInputTriggerChordAction` | Chorded Action | Only while `ChordAction` is triggered; injects a `UInputTriggerChordBlocker` into lower-priority mappings of the same key |

`ETriggerType` decides how triggers combine on one action: `Explicit` (at least one must fire), `Implicit` (all must fire), `Blocker` (blocks everything while `IsBlocking` returns true). `ETriggerEventsSupported` (`None`, `Instant`, `Uninterruptible`, `Ongoing`, `All`) declares which `ETriggerEvent`s a trigger can ever produce.

## Built-in Modifiers

Per-mapping modifiers run first, then the action-level `Modifiers` array; each group runs in array order.

| Class | Display name | Effect |
|---|---|---|
| `UInputModifierDeadZone` | Dead Zone | Zero below `LowerThreshold`, remap to 1 at `UpperThreshold`; `Type` = `Axial`, `Radial`, `UnscaledRadial` |
| `UInputModifierScalar` | Scalar | Multiply per axis by `FVector Scalar` |
| `UInputModifierScaleByDeltaTime` | Scale By Delta Time | Multiply by frame DeltaTime |
| `UInputModifierNegate` | Negate | Invert the axes selected by `bX` / `bY` / `bZ` |
| `UInputModifierSwizzleAxis` | Swizzle Input Axis Values | Reorder axes; `Order` defaults to `EInputAxisSwizzle::YXZ` |
| `UInputModifierSmooth` | Smooth | Rolling average over recent samples |
| `UInputModifierSmoothDelta` | Smooth Delta | Smoothed normalized delta; `SmoothingMethod`, `Speed`, `EasingExponent` |
| `UInputModifierResponseCurveExponential` | Response Curve - Exponential | `sign(x) * pow(abs(x), CurveExponent)` per axis |
| `UInputModifierResponseCurveUser` | Response Curve - User Defined | One `UCurveFloat` per axis |
| `UInputModifierFOVScaling` | FOV Scaling | Scale by camera FOV for constant angular speed across zoom |
| `UInputModifierToWorldSpace` | To World Space | 2D axis → world space |

**WASD → Axis2D** (`AccumulationBehavior = Cumulative` on the action):
- `W`: `Swizzle Input Axis Values (YXZ)` → Y = +1
- `S`: `Swizzle Input Axis Values (YXZ)` + `Negate (bY)` → Y = −1
- `D`: none → X = +1
- `A`: `Negate (bX)` → X = −1

**Gamepad stick**: `Dead Zone (Radial, LowerThreshold = 0.2)` on each stick mapping.
**Mouse look**: `Scalar(0.4, 0.4, 1.0)`, then `Smooth`, then `FOV Scaling` on the mapping.

## Mapping Contexts and Priority

```cpp
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` defaults: `bIgnoreAllPressedKeysUntilRelease = true`, `bForceImmediately = false`, `bNotifyUserSettings = false`. The first one already suppresses ghost inputs from keys held across a context switch — do not set it explicitly thinking it is opt-in; set it to `false` only when you deliberately want a held key to carry into the new context.

`bConsumeInput = true` on a `UInputAction` (the default) means a higher-priority context mapping the same physical key blocks every lower-priority binding of that key. Use priority plus `bConsumeInput` to keep modes apart — a vehicle context consuming Spacebar stops the character's Jump action firing while driving.

`RegistrationTrackingMode` on the IMC: `Untracked` (the first `RemoveMappingContext` wins, whatever the add count) or `CountRegistrations` (stays applied until removed as many times as it was added — use it when several systems share one IMC).

Each local player owns a separate `UEnhancedInputLocalPlayerSubsystem`, so split-screen contexts never leak between players. Fetch a specific player's subsystem with `ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LocalPlayer)` or `ULocalPlayer::GetSubsystemFromController<UEnhancedInputLocalPlayerSubsystem>(PlayerController)`. Simulate input without a device with `InjectInputForAction(Action, RawValue, Modifiers, Triggers)` or `InjectInputVectorForAction(Action, Value, Modifiers, Triggers)`.

## Input Mode Filtering

Gate whole mapping contexts on a gameplay-tag state instead of adding and removing them. Requires `bEnableInputModeFiltering` on `UEnhancedInputDeveloperSettings`.

- The subsystem holds the current mode: `GetInputMode()`, `SetInputMode(Tags, Options)`, `AppendTagsToInputMode`, `AddTagToInputMode`, `RemoveTagsFromInputMode`, `RemoveTagFromInputMode` — all take an optional `FModifyContextOptions`.
- Each `UInputMappingContext` chooses how it is matched via `InputModeFilterOptions`: `UseProjectDefaultQuery` (the project's `DefaultMappingContextInputModeQuery`), `UseCustomQuery` (the IMC's own `InputModeQueryOverride`), or `DoNotFilter`.
- `UEnhancedInputDeveloperSettings::DefaultInputMode` seeds the mode on every new `UEnhancedPlayerInput`.
- Contexts whose query fails stay registered but stop producing input, so priorities and registration counts are untouched.

```cpp
Subsystem->SetInputMode(FGameplayTagContainer(MyGameplayTags::TAG_InputMode_Menu));
```

## Runtime Key Rebinding

`UEnhancedInputUserSettings` (`UserSettings/EnhancedInputUserSettings.h`) is the sanctioned rebinding path. It is a `USaveGame`, created per subsystem when `bEnableUserSettings` is on, and saved to the slot named by `InputSettingsSaveSlotName`.

```cpp
#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();
	}
}
```

Register any IMC the settings UI must list — even one never added to the subsystem — with `RegisterInputMappingContext(IMC)`; adding a context with `bNotifyUserSettings = true` does it for you. Read mappings back with `FindMappingsInRow(MappingName)` and `FindCurrentMappingForSlot(MappingName, Slot)`, reset with `ResetAllPlayerKeysInRow` or `ResetKeyProfileIdToDefault`, and persist with `SaveSettings()` / `AsyncSaveSettings()`. Profile identifiers are `FString`, not `FGameplayTag` — `ProfileIdString`, `GetProfileIdString()`, `GetActiveKeyProfileId()`, `GetKeyProfileWithId()`, `SetActiveKeyProfile()`.

An action or mapping is only rebindable if it carries a `UPlayerMappableKeySettings` (`Name`, `DisplayName`, `DisplayCategory`, `Metadata`, `SupportedKeyProfileIds`) — either on the `UInputAction` or on the `FEnhancedActionKeyMapping` with `SettingBehavior = OverrideSettings`. Full API and struct tables: [references/input-action-reference.md](references/input-action-reference.md).

## Input Without a PlayerController

`UEnhancedInputWorldSubsystem` (Experimental in 5.8) lets actors that will never be possessed receive input delegates. Enable it with `bEnableWorldSubsystem` in `UEnhancedInputDeveloperSettings`.

```cpp
#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);
	}
}
```

Pair every `AddActorInputComponent` with `RemoveActorInputComponent` on `EndPlay`. The subsystem implements the same `IEnhancedInputSubsystemInterface` as the local-player one, so `AddMappingContext`, `InjectInputForAction` and friends behave identically — but it has no user settings and no local player.

## Per-Platform Input Data

`UEnhancedInputPlatformSettings` (`UPlatformSettings`, `UEnhancedInputPlatformSettings::Get()`) holds an `InputData` array of `UEnhancedInputPlatformData` subclasses. Each one defines `MappingContextRedirects`, a `TMap` swapping one `UInputMappingContext` for another on that platform; `GetContextRedirect(Context)` returns the replacement or the original. Use it to substitute a touch or console context without branching in gameplay code. Calling `Get()` needs `DeveloperSettings` in Build.cs (it inlines a `UPlatformSettingsManager` call).

## Custom Triggers

Subclass `UInputTrigger` and override `UpdateState_Implementation`, returning `ETriggerState::None`, `Ongoing` or `Triggered`.

```cpp
// 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)` is `Value.GetMagnitudeSq() >= ActuationThreshold * ActuationThreshold`. For time-based triggers derive from `UInputTriggerTimedBase`, which transitions to `Ongoing` on actuation and gives you `HeldDuration` plus `CalculateHeldDuration(PlayerInput, DeltaTime)` and `bAffectedByTimeDilation`. Override `IsBlocking(const ETriggerState State) const` for blockers and `GetDebugState() const` for the debug HUD. If your trigger keeps state across mapping rebuilds, override `ReceiveTriggerReinstanced_Implementation(const UInputTrigger* OldTrigger)` and call `Super`.

## Custom Modifiers

Subclass `UInputModifier` and override:
```cpp
virtual FInputActionValue ModifyRaw_Implementation(
	const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override;
```

The returned value is converted back to the action's `ValueType` before further processing, so you may return any shape internally — `FInputActionValue(CurrentValue.GetValueType(), Vector)` preserves it. Override `GetVisualizationColor_Implementation(FInputActionValue SampleValue, FInputActionValue FinalValue) const` to control the debug overlay colour. Worked example: [references/input-action-reference.md](references/input-action-reference.md).

## UI Input Mode

Without CommonUI, switch modes on the PlayerController with `SetInputMode(FInputModeUIOnly())`, `SetInputMode(FInputModeGameAndUI())` or `SetInputMode(FInputModeGameOnly())`.

CommonUI routes input through `UCommonActivatableWidget` stacks and removes most manual `SetInputMode` calls. Enhanced Input and Common Input are unified: turn on `UCommonInputSettings::bEnableEnhancedInputSupport` (Project Settings → Game → Common Input; requires a restart) and CommonUI's click and back actions become `UInputAction` assets — `UCommonUIInputData::EnhancedInputClickAction` and `EnhancedInputBackAction` — instead of data-table rows. Widget stacks, activatable widgets and input configs belong to `ue-ui-umg-slate`.

## Debugging

`ShowDebug EnhancedInput` prints the applied mapping contexts, each action's value and trigger state, and each trigger's `GetDebugState()`. `ShowDebug WorldSubsystemInput` does the same for `UEnhancedInputWorldSubsystem`; `ShowDebug InputSettings` dumps the developer settings; `LogWorldSubsystemInput` at `VeryVerbose` logs every key the world subsystem processes.

## Deprecated — do not use

| Do not emit | Use in 5.8 | Source |
|---|---|---|
| `UInputTriggerCombo` | No replacement — build the sequence from `UInputTriggerChordAction`, timers, or a state machine | `UE_DEPRECATED(5.8)` in `InputTriggers.h:571` |
| `FInputComboStepData`, `FInputCancelAction` | No replacement | `UE_DEPRECATED(5.8)` in `InputTriggers.h:530,551` |
| `FEnhancedInputKeys::ComboKey` | No replacement | `UE_DEPRECATED(5.8)` in `EnhancedInputModule.h:20` |
| `UInputMappingContext::Mappings` | `DefaultKeyMappings` / `GetMappings()` | `UE_DEPRECATED(5.7)` in `InputMappingContext.h:93` |
| `UEnhancedInputPlatformSettings::LoadInputDataClasses()`, `InputDataClasses` | `GetInputData()` / `ForEachInputData()` | `UE_DEPRECATED(5.7)` in `EnhancedInputPlatformSettings.h:77,88` |
| `UEnhancedPlayerInput::GetAppliedInputContexts()` | `GetAppliedInputContextData()` | `UE_DEPRECATED(5.6)` in `EnhancedPlayerInput.h:175` |
| `FMapPlayerKeyArgs::ProfileId` (`FGameplayTag`), `SetKeyProfile(FGameplayTag)`, `GetCurrentKeyProfile()`, `GetCurrentKeyProfileIdentifier()`, `GetAllSavedKeyProfiles()`, `GetKeyProfileWithIdentifier()`, `ResetKeyProfileToDefault(FGameplayTag)`, `GetProfileIdentifer()` | `ProfileIdString` (`FString`), `SetActiveKeyProfile(FString)`, `GetActiveKeyProfile()`, `GetActiveKeyProfileId()`, `GetAllAvailableKeyProfiles()`, `GetKeyProfileWithId()`, `ResetKeyProfileIdToDefault(FString)`, `GetProfileIdString()` | `UE_DEPRECATED(5.6)` in `UserSettings/EnhancedInputUserSettings.h:86,706,730,722,754,776,673,327` |
| `UPlayerMappableKeySettings::SupportedKeyProfiles` | `SupportedKeyProfileIds` (`TArray<FString>`) | `UE_DEPRECATED(5.6)` in `PlayerMappableKeySettings.h:62` |
| `UEnhancedInputDeveloperSettings::bShouldLogAllWorldSubsystemInputs` | `LogWorldSubsystemInput` at `VeryVerbose` | `UE_DEPRECATED(5.6)` in `EnhancedInputDeveloperSettings.h:150` |
| `UPlayerMappableInputConfig` | `UEnhancedInputUserSettings` | `UE_DEPRECATED(5.3)` in `PlayerMappableInputConfig.h:23` |
| `UInputComponent::BindAxis`, `BindAction(FName, EInputEvent, Object, Func)`, `BindAxisKey`, `BindVectorAxis`, `BindKey`, `BindTouch`, `BindGesture` | `UEnhancedInputComponent::BindAction(UInputAction*, ETriggerEvent, Object, Func)` | `= delete` on `UEnhancedInputComponent`, `EnhancedInputComponent.h:613-634` |

## Common Mistakes

**Adding the mapping context in `SetupPlayerInputComponent`:** that call can run before the controller and local player are resolved. Add contexts in `BeginPlay`, `OnPossess`/`PossessedBy`, or a `PlayerControllerChanged` handler.

**Setting `bIgnoreAllPressedKeysUntilRelease = true` "to be safe":** it is already the default on `FModifyContextOptions`. Only touch it to set `false`.

**Binding `Triggered` for a button press:** `Triggered` fires every active frame. Use `Started` for press-once and `Completed` for release.

**Expecting `Completed` alongside `Ongoing`:** `Completed` is suppressed on any tick where a trigger reports `Ongoing`. Split into two actions with their own triggers.

**No dead zone on sticks:** analog sticks rest at non-zero. Add `Dead Zone (Radial)` to every stick mapping.

**Missing swizzle for WASD:** a keyboard key produces a 1D value on X. Without `Swizzle Input Axis Values (YXZ)` on W and S, forward/back never reaches the Y component of an `Axis2D` action.

**Calling `MapKey`/`UnmapKey` for player rebinding:** those mutate the IMC asset and are editor/config helpers. Use `UEnhancedInputUserSettings::MapPlayerKey` instead, and pass `bNotifyUserSettings = true` when adding contexts you want listed in a rebinding UI.

**Replicating trigger events:** input is client-local. Replicate the result — movement input, ability activation — never the `ETriggerEvent`.

**Legacy binding on a `UEnhancedInputComponent`:** `BindAxis` and the `FName` `BindAction` overloads are `= delete` and fail to compile. Migrate them rather than defining `ENHANCED_INPUT_ALLOW_LEGACY_BINDING=1`.

## Related Skills

- `ue-gameplay-framework` — `APlayerController`, possession, `SetupPlayerInputComponent` lifecycle
- `ue-ui-umg-slate` — CommonUI activatable-widget stacks, input configs, cursor and focus
- `ue-character-movement` — consuming `FInputActionValue` through `AddMovementInput`
- `ue-gameplay-abilities` — activating abilities from input, `UAbilitySystemComponent` input IDs
- `ue-actor-component-architecture` — `UActorComponent`/`UInputComponent` ownership, `EnableInput`, component-owned input
- Also relevant: `ue-cpp-foundations`, `ue-game-features`, `ue-gameplay-cameras`, `ue-mover`

© quodsoler · MIT · quodsoler/unreal-engine-skills

Похожие скиллы

Всё по теме «Геймдев и 3D: Unity, Unreal, Godot, Blender» →
unreal-enhanced-input gamedev-skills Помогает настроить систему ввода Enhanced Input в Unreal Engine 5. Полезен при сборке ассетов действий, привязке событий в C++ или Blueprints и отладке управления. CX Codex +1 5 200установок unreal-engine DSTN2000 Скилл обучает ассистента исследовать структуру проекта Unreal Engine перед написанием кода. Он находит файлы .uproject, сканирует ассеты и настраивает C++, Enhanced Input или GAS. CX Codex +1 511установок unreal-engine-cpp-pro sickn33 Скилл для разработки на C++ в Unreal Engine 5.x: правила работы с UObject, сборщиком мусора, отражением и производительностью. CX Codex +1 1 400установок unreal-cpp-gameplay gamedev-skills Учит агента писать геймплейный C++ под Unreal Engine 5: макросы рефлексии, классы Gameplay Framework, зависимости модуля в *.Build.cs. CX Codex +1 5 300установок asset-audit Donchitos Проверяет ассеты игры на соответствие правилам именования и лимитам веса, находя забытые файлы и битые ссылки в коде. CX Codex +1 469установок unreal-engine-skills-for-claude-code Epic Games Плагин даёт Claude доступ к запущенному Unreal Editor через MCP: акторы, блюпринты, материалы, Niagara, Sequencer и другие инструменты. ★ 1 634звёзд репо

Из того же набора25

ue-project-context Собирает файл .agents/ue-project-context.md, из которого остальные UE-скиллы узнают версию движка, модули, плагины и конвенции проекта. CX Codex +1 1 300установок ue-editor-tools Скилл для работы с C++-расширениями редактора Unreal Engine: кастомизация панели Details, меню, editor utility widgets, режимы и подсистемы. Пригодится, когда пишешь editor-модуль. CX Codex +1 1 300установок ue-ui-umg-slate Помогает писать и чинить интерфейсы Unreal Engine 5.8 на C++: UMG, Slate, экраны Common UI и вьюмодели MVVM. CX Codex +1 1 400установок ue-data-assets-tables Скилл помогает проектировать игровые данные в C++ под Unreal Engine 5.8: настраивать Data Assets, таблицы, мягкие ссылки и асинхронную загрузку через Asset Manager. CX Codex +1 1 200установок ue-actor-component-architecture Помогает писать и отлаживать C++ код акторов и компонентов в Unreal Engine 5.8: настраивает жизненный цикл, спавн, тики и прикрепление компонентов без ошибок инициализации. CX Codex +1 1 200установок ue-gameplay-framework Скилл для работы с классами геймплейного фреймворка Unreal Engine: GameMode, GameState, PlayerController, Pawn и потоками входа игрока, спавна и урона. CX Codex +1 1 200установок ue-niagara-effects Скилл для работы с Niagara из C++ в Unreal Engine 5.8: спавн систем, User-параметры, Data Channels, пулинг компонентов. CX Codex +1 1 200установок ue-materials-rendering Помогает писать C++ код материалов и рендеринга для Unreal Engine 5.8: динамические инстансы, коллекции параметров, Render Targets и шейдеры. CX Codex +1 1 200установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой