前言

UE5 使用了一个最新的输入架构EnhancedInput(增强输入系统)

简介

增强输入框架就是改进一些过往问题的新答案。它以模块化的方式解耦了从输入的按键配置到事件处理的逻辑处理过程,通过提供输入动作(UInputAction)输入修改器(UInputModifier)输入触发器(UInputTrigger输入映射环境(UInputMappingContext)这些可组合功能,在新的增强玩家输入(UEnhancedPlayerInput)和增强输入组件( UEnhancedInputComponent)的配合下提供了更灵活和更便利的输入配置和处理功能。
考虑到基于 虚幻引擎5(UE5)的项目可能需要更多高级输入功能,例如复杂的输入处理、运行时重新映射输入,我们开发了增强输入插件(Enhanced Input Plugin),以便开发者拥抱次世代项目,同时又能向后兼容 虚幻引擎4(UE4)中的默认输入系统。

目标:

  • 重新梳理简化: Axis/Action —> Action(统一)

  • 运行时重新映射输入场景: UInputMappingContext

  • 对初级用户易配置。大量默认行为实现,Tap/Hold…

  • 对高级用户易扩展,可继承子类扩展

    • 修改器:修改输入值
    • 触发器:决定触发条件
    • 优先级:配置输入场景优先级
  • 模块化,不再只依赖Ini配置,以资源asset方式配置,堆栈式分隔逻辑

  • 提高性能,不需要检查所有输入,只需要关心当前的场景和绑定

  • UE5正式替换掉旧有输入系统

关键词

○ IC: Input Component 输入组件

○ IA: Input Actions 输入动作

○ IMC: Input Mapping Contexts (输入映射上下文 IA 的集合)

○ PMI: Player Mappable Input Config (玩家可映射输入配置 IMC 的集合)

○ IM: 输入修饰符 Input Modifies

○ IT: 输入触发器 InputTriggers

IMC

一套当前的Key->InputAction的映射集合

多个IMC同时作用,高优先级的会先处理,如果没有则触发到低优先级的

高优先级的Key绑定会屏蔽低优先级的绑定

流程图

Input处理流程

下载 (1)

硬件平台与Input关系

下载

Trigger 与 UI 事件 对应关系

1
2
3
4
5
6
7
8
UInputTriggerDoubleTap    --> OnDoubleClicked
UInputTriggerDown --> OnDownClicked
UInputTriggerPressed --> OnPressed
UInputTriggerReleased --> OnReleased
UInputTriggerHold --> OnHold
UInputTriggerHoldAndRelease --> OnHoldAndRelease
UInputTriggerPulse --> OnPulse
UInputTriggerTap --> OnTap
下载 (2)

CPP绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//MyCharacter.h 多数省略
/**
* TObjectPtr: 增加在SHIPPING发布之前的安全性
* 想处理什么Action,就响应什么动作,不太关心动作是由什么按键触发的
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputMappingContext> InputMappingContext;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_MoveForward;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_MoveRight;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_Turn;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_LookUp;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_Jump;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="EnhancedInput|Action", meta=(AllowPrivateAccess="true"))
TObjectPtr<UInputAction> IA_Fire;

//MyCharacter.cpp
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);

// 先添加IMC还是绑定IA不重要,因为IMC代表的只是一个从按键到Action的关系,下面的Action是怎么到一个回调
if(APlayerController* PC = CastChecked<APlayerController>(GetController()))
{
if(UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
{
Subsystem->AddMappingContext(InputMappingContext, 100);
}
}

if(UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
if(IA_MoveForward)
{
EnhancedInputComponent->BindAction(IA_MoveForward, ETriggerEvent::Triggered, this, &AMyCharacter::MoveForward);
}

if(IA_MoveRight)
{
EnhancedInputComponent->BindAction(IA_MoveRight, ETriggerEvent::Triggered, this, &AMyCharacter::MoveRight);
}

if(IA_Turn)
{
EnhancedInputComponent->BindAction(IA_Turn, ETriggerEvent::Triggered, this, &AMyCharacter::TurnAtRate);
}

if(IA_LookUp)
{
EnhancedInputComponent->BindAction(IA_LookUp, ETriggerEvent::Triggered, this, &AMyCharacter::LookUpAtRate);
}

if(IA_Jump)
{
EnhancedInputComponent->BindAction(IA_Jump, ETriggerEvent::Started, this, &AMyCharacter::OnJump);
EnhancedInputComponent->BindAction(IA_Jump, ETriggerEvent::Completed, this, &AMyCharacter::OnStopJumping);
}
}
}

QA

为什么IMC和IA都有Triggers和Modifiers?

因为一个操作配置在IA里面相当于是全局的,如果两个都配了,两个都起作用,先是IMC里的起作用,再是IA里起作用。(比如:IMC、IA里都配了Negate,那么最终的值就是正的。)

如果有一个重攻击的Action,在IA里配上相应的全局Trigger,在各个不同的IMC里只需要改按键,不需要每一个再配一套Trigger。(IA全局 IMC当前)实践

IMC BindAction

  • 初始情况应该在哪里开始应用IMC
  • 后续运行时在蓝图中如何切换IMC
  • 何时Remove IMC
  • 在哪里绑定Action和Axis
  • 在蓝图中如何BindAction

实践

初始情况应该在哪里开始应用IMC

img

img

何时Remove IMC

img

IMC BindAction最佳实践

  • 分层的IMC设计:基本输入(移动),武器/载具,行为/Buff
  • Add(IMC,Priority),规划好Priority的值, IMC的Priorit体现的是Key和InputAction之间的映射关系,但是找到InputAction之后,还是依然按照InputStack的顺序来处理。
  • Pawn上可携带多个IMC,但只Apply一个 IMC不一定跟Pawn绑定关联,可根据运行时逻辑灵活Add
  • IMC代表输入的逻辑处理环境,BindAction代表输入事件该由谁来处理的职责

img

Debug

调试手段

ShowDebug EnhancedInput 显示调试界面

Input.+action ActionName Value 强制添加某个 action 的输入

Input.-action ActionName 移除某 Action 的输入

Input.+key key Value 添加某 Key 的输入

Input.-key keyName 移除某 key 的输入

参考

Lyra 待更新

UE5-Lyra中的输入系统(增强输入) - 知乎

UE5 – EnhancedInput(增强输入系统) - 知乎

虎跳龙拿–新一代增强输入框架EnhancedInput | Epic 大钊

EnHanced Input - 知乎