🎮

【UE5/C++】EnhancedInput.InputActionから入力値を取得する

2024/09/09に公開

はじめに

バージョンはUnrealEngine 5.4.4

やりたいこと


BPだと、このノード一つで値を取ってくることができる。
C++でこのノードの働きをする実装がしたい。

結論

ちょっと調べてみて、出てきた方法は以下の感じ。

// ActionValueのバインド(入力値取得のための準備)
FEnhancedInputActionValueBinding* MoveActionValueBinding;
MoveActionValueBinding = &EnhancedInputComponent->BindActionValue(MoveAction);

// 入力値を取得する
FVector2D InputActionValue = MoveActionValueBinding->GetValue()->Get<FVector2D>();

最終的には、FInputActionValue::Get<T>で値を取得する。
FEnhancedInputActionValueBindingは、UEnhancedInputComponent::BindActionValue()
で生成されるオブジェクトで、アクションに対して既に生成済みならば生成済みのオブジェクトが返される。

BindActionValueをしなけりゃ、FEnhancedInputActionValueBindingは生成されず、
でもBindActionでバインドした処理はどこからともなく上手いこと入力値を受け取っている。
じゃあ、FEnhancedInputActionValueBindingの生成は必須じゃないの?無駄なの?
気になることは調べてみることにしました。

詳しく見てみる

BindActionValue()って何をしてるの?
FEnhancedInputActionValueBindingって何?

BindAction()でバインドした処理はどうやって入力値を受け取るの?

UEnhancedInputComponent::BindAction()で以下のように処理をバインドした場合、

EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered,
                                   this, &EnhancedInputCharacter::Move);

EnhancedInputCharacter::Moveを以下のように宣言していると、

void Move(const FInputActionValue& Value)

以下のメソッドによって処理が呼び出される。

void FEnhancedInputActionEventDelegateBinding<FEnhancedInputActionHandlerValueSignature>::Execute(const FInputActionInstance& ActionData)
{
    Delegate.Execute(ActionData.GetValue());
}

このExecuteメソッドの呼び出し元はUEnhancedPlayerInput(::EvaluateInputDelegates)で、
UEnhancedPlayerInputが管理しているFInputActionInstanceを渡して、
バインドした処理はそれを受け取って入力値を取得している、というのが全体の流れのよう。

で、FInputActionInstanceってのは何者?

プレイヤーごとに生成されるUInputActionのインスタンスと、入力値ことValue。
ValueはFInputActionValueという型で保持していて、これの本体はFVector型。
ValueTypeごとに、FVectorのどの要素を見るか切り替えるような実装になっている。

  • Digital(bool):FVectorがゼロベクトルならFalse、それ以外はTrue
  • Axis1D(float):FVectorのX要素のみを扱う
  • Axis2D(FVector2D):FVectorのX、Y要素のみを扱う
  • Axis3D(FVector):そのまんま

じゃあ、FEnhancedInputActionValueBindingって何者だったの?

アクションの入力値を後で参照できるようにするためのオブジェクトで、
中身はUInputActionのインスタンスを参照するWeakObjectPtrと、mutableなValue。
このValueの更新は、UEnhancedPlayerInput(::EvaluateInputDelegates)で行われて、
さきほどのExecuteメソッドの実行直後くらいに実施される流れのよう。

プレイヤーごとに生成されたUInputActionのインスタンスであるFInputActionInstance
それを外部から参照するためのコピーとして生成されるFEnhancedInputActionValueBinding
という感じの構造になっているのかなと。

結局、どう実装するか

コピーなんて無駄無駄ァ!
という場合、UEnhancedPlayerInput::FindActionInstanceData()から
FInputActionInstanceを探してきて、そこから入力値を取得することもできますね。
(EnhancedPlayerInputは、EnhancedInputLocalPlayerSubsystemからGetPlayerInputできる)

const FInputActionInstance* InputActionInstance = EnhancedPlayerInput->FindActionInstanceData(MoveAction);
if (InputActionInstance)
{
    FVector2D Value = InputActionInstance->GetValue().Get<FVector2D>();
    UKismetSystemLibrary::PrintString(this, FString::Printf(TEXT("Value: %f"), Value.Length()));
}

FindActionInstanceData()は名前の通り検索処理なので、毎フレーム実行はしない方がよいとして、
わざわざFEnhancedInputActionValueBindingなるものが用意されているのを考えるに、
そっちを使った方が良いのかな?Unbindしたあととか問題になるのかも。
気にするほど無駄があるわけでもないので、大人しく結論に記載した実装を使えばよさそうです。

Discussion