c4.2Subsystem|EnhancedInput
大约 9 分钟
导读
EnhancedInput|增强输入系统
1.插件
2.项目设置
3.输入动作(Input Actions)
4. 输入映射上下文(Input Mapping Contexts)
历史问题
- 复杂的输入机制无法满足。
- 过于简陋,例如按住、双击、联合输入等都需要用户自己实现。
- 性能不足,需要自己写切换逻辑,不同情况需要自己判断优先级。
5. 触发状态(Trigger State)
原版
enum class ETriggerEvent : uint8
{
// No significant trigger state changes occurred and there are no active device inputs
None = (0x0) UMETA(Hidden),
// Triggering occurred after one or more processing ticks
Triggered = (1 << 0), // ETriggerState (None -> Triggered, Ongoing -> Triggered, Triggered -> Triggered)
// An event has occurred that has begun Trigger evaluation. Note: Triggered may also occur this frame.
Started = (1 << 1), // ETriggerState (None -> Ongoing, None -> Triggered)
// Triggering is still being processed
Ongoing = (1 << 2), // ETriggerState (Ongoing -> Ongoing)
// Triggering has been canceled
Canceled = (1 << 3), // ETriggerState (Ongoing -> None)
// The trigger state has transitioned from Triggered to None this frame, i.e. Triggering has finished.
// NOTE: Using this event restricts you to one set of triggers for Started/Completed events. You may prefer two actions, each with its own trigger rules.
// TODO: Completed will not fire if any trigger reports Ongoing on the same frame, but both should fire. e.g. Tick 2 of Hold (= Ongoing) + Pressed (= None) combo will raise Ongoing event only.
Completed = (1 << 4), // ETriggerState (Triggered -> None)
};
翻译
enum class ETriggerEvent : uint8
{
// 没有发生显著的触发状态变化,且没有活动的设备输入
None = (0x0) UMETA(Hidden),
// 在一个或多个处理帧之后触发
Triggered = (1 << 0), // ETriggerState (None -> Triggered, Ongoing -> Triggered, Triggered -> Triggered)
// 发生了开始触发器求值的某个事件。注意:本帧也可能发生 Triggered 状态。
Started = (1 << 1), // ETriggerState (None -> Ongoing, None -> Triggered)
// 触发仍在进行处理
Ongoing = (1 << 2), // ETriggerState (Ongoing -> Ongoing)
// 触发已被取消
Canceled = (1 << 3), // ETriggerState (Ongoing -> None)
// 触发状态在本帧从 Triggered 转换为 None,即触发已完成。
// 注意:使用此事件将限制您对于 Started/Completed 事件的触发规则。您可能更喜欢两个动作,每个都有自己的触发规则。
// TODO: 如果同一帧内任何触发器报告 Ongoing,则 Completed 不会触发,但两者都应触发。例如,Hold(= Ongoing) + Pressed(= None)组合的第 2 帧将仅触发 Ongoing 事件。
Completed = (1 << 4), // ETriggerState (Triggered -> None)
};
6. 修饰符(Modifiers)
增强输入支持来自一维源的输入,例如键盘的方向键或常用的"WASD"键配置;可通过应用正确的输入修饰符来实现此控制方案。 具体而言,使用 负(Negate) 可以将某些键注册为负值, 而使用 交换输入轴值(Swizzle Input Axis Values) 可以将某些键注册为Y轴,而不是默认的X轴值:
7. 触发器(Triggers)
8.绑定上下文
快速上手
1. 插件
2. Build.cs引入模块
PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput" });
3. 准备Character
你也可以直接用第三人称模板作为参考,这里主要是康康基本流程为后续源码展开做一个铺垫
4. 引入头文件
MyCharacter.h
#include "InputActionValue.h"
MyCharacter.cpp
#include "Components/InputComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
5.子系统注册上下文
MyCharacter.h
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputMappingContext* DefaultMappingContext;
MyCharacter.cpp
void ATP_ThirdPersonCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
//Add Input Mapping Context
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
6.绑定InputAction
MyCharacter.h
/** Move Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveAction;
protected:
// APawn interface
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
/** Called for movement input */
void Move(const FInputActionValue& Value);
MyCharacter.cpp
//////////////////////////////////////////////////////////////////////////
// Input
void ATP_ThirdPersonCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
//Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ATP_ThirdPersonCharacter::Move);
}
}
void ATP_ThirdPersonCharacter::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller != nullptr)
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
//Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ATP_ThirdPersonCharacter::Move);
/**
* Binds to an object UFUNCTION
*/
FEnhancedInputActionEventBinding& BindAction(const UInputAction* Action, ETriggerEvent TriggerEvent, UObject* Object, FName FunctionName)
{
TUniquePtr<FEnhancedInputActionEventDelegateBinding<FEnhancedInputActionHandlerDynamicSignature>> AB = MakeUnique<FEnhancedInputActionEventDelegateBinding<FEnhancedInputActionHandlerDynamicSignature>>(Action, TriggerEvent);
AB->Delegate.BindDelegate(Object, FunctionName);
AB->Delegate.SetShouldFireWithEditorScriptGuard(bShouldFireDelegatesInEditor);
return *EnhancedActionEventBindings.Add_GetRef(MoveTemp(AB));
}
7.Triggers是什么?
先看源码
InputAction.h原文
/**
* Trigger qualifiers. If any trigger qualifiers exist the action will not trigger unless:
* At least one Explicit trigger in this list has been met.
* All Implicit triggers in this list are met.
*/
UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = Action)
TArray<TObjectPtr<UInputTrigger>> Triggers;
InputAction.h 翻译
/**
* 触发条件修饰符。如果存在任何触发条件修饰符,则该动作只有在以下情况下才会触发:
* 至少一个明确触发条件在此列表中已满足。
* 此列表中的所有隐含触发条件都已满足。
*/
UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = Action)
TArray<TObjectPtr<UInputTrigger>> Triggers;
注
在C++中,关键字 final
用于表示某个类不能被其他类继承。例子中,UInputTriggerDown
类声明为 final
,这意味着它是不可继承的,不能再派生出其他类。
- 性能优化: 编译器可以对
final
类进行更多的优化,因为它知道没有其他类会继承它。 - 代码安全性: 防止其他开发者错误地继承并修改该类的行为,从而确保该类的稳定性。
8.系统流程
9.子系统
UEnhancedInputLocalPlayerSubsystem : public ULocalPlayerSubsystem, public IEnhancedInputSubsystemInterface
UEnhancedInputWorldSubsystem : public UWorldSubsystem, public IEnhancedInputSubsystemInterface
Debug
showdebug enhancedinput
参考资料
UE5学习笔记|增强输入系统EnhancedInput_鹿野明的博客-CSDN博客
虚幻引擎中的增强输入 | 虚幻引擎5.1文档(unrealengine.com)
UE5 C++ Enhanced Input - 2 - Bind C++ Functions to Input Actions - YouTube