こんにちは!
情熱開発部・プログラム課の長谷です。
最近は暑かったと思えば、急激に気温が下がってきて、
体調管理が難しい時期になってきました。
管理が難しいと言えば、ゲームの入力情報の管理です。
そんな入力管理を快適にしてくれる
「EnhancedInput」
について紹介していこうと思います!
環境
UnrealEngine5.3.2を使用しています。
EnhancedInputとは?
UnrealEngine独自のプラグインで、
入力管理を行うことができるフレームワークになります。
キーやデバイスに依存しない形で「入力アクション」を定義することができ、柔軟で直感的に管理できます。
事前準備
まず、この機能を使うための設定をしていきます。
プロジェクト名Build.csを開き、以下のように"EnhancedInput"
と追加する必要があります。
(このバージョンでは初期設定されています)

入力アセットについて
入力のやり取りには、以下の2つのアセットが必要になります。
- 入力アクション
- 入力マッピングコンテキスト(以下「入力マップ」という)

入力アクション
入力アクションは、
「ジャンプ」=入力アクション
のように1つの動作に対して作成します。
設定項目については、主に以下の3つを使います。

1. 入力軸(ValueType)
入力時の取得する値の型を指定します。

2. 入力トリガー(Triggers)
アクションが実行される条件の定義を行えます。
例)Downを選択した場合、入力中は常にトリガーONになります。

トリガーの種類については、以下のサイトを参考にしていただければと思います!
【UE5.0.x/UE5.1】Enhanced Input の備忘録
3. モディファイア(Modifiers)
受け取った入力値に対して、変換・修正します。
例)Negateの場合、入力値が「+1.0」のだった時、「-1.0」に反転して返てくれます。

この作成した入力アクションは次のアセットで使用します!
入力マップ
入力マップは、
入力アクションと物理デバイス(ボタンやキーなど)を紐づけることができます。


一つの入力アクションに対し、複数のキーを設定することができ、
それによって、同じアクション内で異なるデバイスのキーを設定することができたりもします。
これで準備は完了です!
実際に動かしてみる
今回はキャラクターをジャンプをさせていきます!
(ブループリント、C++の両方で紹介します)
アセット作成
クラスの作成
ブループリント、またはC++でCharacterを継承したクラスの作成をします。


入力アクションの作成と設定
ジャンプをするためのアクションになります。

今回はPressedを設定しましたが、
未設定の場合以下のようになります。
入力方法 | イベント名 |
---|---|
入力した瞬間 | Started, Triggered |
長押し入力中 | Triggered |
入力を終了 | Completed |
入力マップの作成とアクション登録
先ほど作成した、ジャンプ用のアクションとキーの設定をします。

ブループリント
1 )入力マップの登録
作成したキャラクターブループリントクラスを開いて、以下のノードを作成します。

AddMappingContextノードに作成したアセットを設定しておきます。

2 )アクションの呼び出し
入力アクション名をノード検索すると、
以下のようなイベント関数がでてくるので、
そのノードと呼び出したい関数ノードを接続します。

これでジャンプさせることができました!
非常に楽しそうに飛んでくれて嬉しいです。

C++
こちらも作成したキャラクタークラスを使用していきます!
ヘッダーはこんな感じで作成しました。
// 前方宣言
class UInputMappingContext;
class UInputAction;
struct FInputActionValue;
UCLASS()
class ACharactorInput_Blog : public ACharacter
{
GENERATED_BODY()
public:
ACharactorInput_Blog();
protected:
virtual void BeginPlay() override;
public:
// ジャンプ関数
void InputAction_Jump(const FInputActionValue& InputActionValue);
protected:
// 入力マップ
UPROPERTY(EditAnywhere, Category = "Input")
TSoftObjectPtr<UInputMappingContext> InputMapping;
// インプットアクション
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* InputAction;
};
.cpp側のincludeになります。
#include "Charactor/CharactorInput_Blog.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "InputActionValue.h"
#include "InputMappingContext.h"
#include "InputAction.h"
1 ) 入力時に呼び出す関数の作成
FInputActionValueを引数にして関数を作成します。
→指定したValueTypeにもとづいた型のデータを取得できます。
※今回はこの関数が呼ばれるだけで問題ないので、コメントアウトしています。
void ACharactorInput_Blog::InputAction_Jump(const FInputActionValue& InputActionValue)
{
/*
EInputActionValueType ValueType = InputActionValue.GetValueType();
if (ValueType == EInputActionValueType::Boolean)
{
bool bIsPressed = InputActionValue.Get<bool>();
}
*/
// ジャンプさせる
Jump();
}
2 )入力アクションを設定
▼入力アクションのバインド
UEnhancedInputCompoentのBindActionを使って設定していきます。
UEnhancedInputComponent::BindAction(UInputAction, ETriggerEvent, UserClass, Func);
BindActionの引数
1. UInputAction・・・入力アクションオブジェクト
2. ETriggerEvent・・・入力アクションが実行されるタイミング
3. UserClass・・・呼び出すクラス(基本的にプレイヤー自身)
4. Func・・・バインドする関数。入力を受け付けたときに実行される。
こんな感じで作成してみました。
4の関数には、先ほど作成したジャンプ関数を入れました。
// 入力アクションの設定
if (UEnhancedInputComponent* Component = CastChecked<UEnhancedInputComponent>(InputComponent))
{
// 入力アクションのバインド
Component->BindAction(InputAction, ETriggerEvent::Triggered, this, &ACharactorInput_Blog::InputAction_Jump);
}
3 )入力マップの登録と設定
以下の関数を使って作成した入力マップの登録をしていきます。
UEnhancedInputLocalPlayerSubsystem::AddMappingContext(MappingContext, Priority)
AddMappingContextの引数
1. MappingContext・・・登録したい入力マップ
2. Priority・・・入力の優先度。数字の大きい方の優先度が高くなります。
最終的には以下のような形になりました。
void ACharactorInput_Blog::BeginPlay()
{
Super::BeginPlay();
// 入力アクションのロード
InputAction = LoadObject<UInputAction>(NULL, TEXT("/Game/Asset/Input/InputAction/IA_Jump.IA_Jump"), NULL, LOAD_None, NULL);
// 入力マップのロード
InputMapping = LoadObject<UInputMappingContext>(NULL, TEXT("/Game/Asset/Input/Mapping/IMC_Blog.IMC_Blog"), NULL, LOAD_None, NULL);
// 入力アクションの設定
if (UEnhancedInputComponent* Component = CastChecked<UEnhancedInputComponent>(InputComponent))
{
// 入力アクションのバインド
Component->BindAction(InputAction, ETriggerEvent::Triggered, this, &ACharactorInput_Blog::InputAction_Jump);
}
if (const APlayerController* PlayerController = Cast<APlayerController>(GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* InputSystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
// 既存の入力マップの削除
InputSystem->ClearAllMappings();
// InputMapppingContextの設定
int32 Priority = 0;
InputSystem->AddMappingContext(InputMapping, Priority);
}
}
}
これでジャンプをさせることができましたが、
さらに手を加えてみようと思います。
動的な入力の切り替え
入力マップを未設定の状態で、動的に追加設定してみます。

入力キーの登録
MapKey関数で、指定した入力アクションとキーの紐づけを行えます。
UInputMappingContext::MapKey(UInputAction, FKey);
この処理をBeginPlayのBindAction前に追加しました。
// 入力アクションの設定
~~~~~~~
// キーの追加
if (InputAction)
{
InputMapping->MapKey(InputAction, FKey(FName("SpaceBar")));
}
キーの定義名については、以下サイトにまとめられていましたのでご参考ください。
List of Key/Gamepad Input Names
キーの切り替え
ジャンプ関数を以下のように改造してみました。
ヘッダに適当なフラグを作成し、
そのON・OFFによって、スペースキーとシフトキーが切り替わるようにしてみました。
.h
bool InputFlag = false;
.cpp
void ACharactorInput_Blog::InputAction_Jump(const FInputActionValue& InputActionValue)
{
bool value = InputActionValue.Get<bool>();
Jump();
// キーの変更
for(auto& KeyMap : InputMapping->GetMappings())
{
auto& IA = KeyMap.Action;
if (IA.GetFName() == FName("IA_Jump"))
{
// 現在のキーの削除
InputMapping->UnmapKey(IA, KeyMap.Key);
// 新たなキーの追加
if(InputFlag)
{
InputMapping->MapKey(IA, FKey(FName("SpaceBar")));
InputFlag = false;
}
else
{
InputMapping->MapKey(IA, FKey(FName("LeftShift")));
InputFlag = true;
}
}
}
}
実装内容の確認
実装内容は以下のような流れになっています。
- 実行して、初期化でアクションの自動追加
- ジャンプでスペースキーからシフトキーに切り替え
- 再度ジャンプでスペースキーに戻る

気分によって飛び方を変える、手のかかるかわいい子になりました。
まとめ
EnhancedInputは、次のような利点のある便利なシステムです。
- 入力マッピングは柔軟性があり、複数のデバイスにも対応することができる
- アクションの抽象化によって、特定のキーやボタンに依存せず、変更も簡単にできる
- ランタイムの入力設定の切り替えが可能
他にもまだ様々な便利なシステムがありますので、
自分なりの入力システムを作ってみてはいかがでしょうか。
参考
【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。