関連ブログ
- [UE5] 元の位置に戻るカメラの実装 2024.12.18UE
- [UE5]難易度変更に対応したシューティングゲームを作ってみよう 2024.12.11UE
- [UE5] インタラクト可能なモノの量産に役立つBPを作ってみよう 2024.12.04UE
CATEGORY
2022.05.02UE5
執筆バージョン: Unreal Engine 5.0 |
ハローアンリアル!エンジニアの片平です!
UE4.26から実装された Enhanced Input プラグインですが、
入力周りの痒い所に手が届く、非常に有用なプラグインとなっております。
拡張もしやすい設計となっているため、今回はUI操作に活用した例をご紹介します。
具体的には動画のようにパッドで上下のキーを押し続けるとカーソルが高速移動する InputTrigger を作成します。
なお、Enhanced Inputプラグインは最新のUE5.0.1で「Beta」となっております。
今後のアップデートによっては仕様が変更される可能性があります。ご留意ください
Enhanced Inputプラグインの導入に関しては、本記事の趣旨ではないため割愛します。
下記のページが参考になるかと思います。
入力値 – Unreal Engine 5 Documentation
ThirdPersonTemplateから作り始めました。
EnhanceInputをセットアップし、簡単なメニュー画面を作成しました。
パッドの上下キーで星座を選択できます。
InputMappingContext はキャラクター操作時に使用する「ActionInputContext」と、
メニュー操作時に使用する「UIInputContext」に分けています。
上記の2つを画像のようなBPで切り替えて使用しています。
このとき、ModifyContextOptionsの Ignore All Pressed Keys Until Release をTrueにしないと、
Contextが切り替わった瞬間に、現在押しているボタンのTriggerが走ってしまいます。
例えば、メニューを開くボタンと閉じるボタンを同じボタンに設定しているときに
メニューを開く → メニューを閉じる → メニューを開く → メニューを閉じる → メニューを開く → (以下略)
という感じで無限ループとなるため、このような使い方をするときは必ず Ignore All Pressed Keys Until Release を Trueにしましょう。
UIへの繋ぎこみは、インターフェースを使用しています。
InputTrigger (UInputTrigger)とは入力に対して、
どのような条件で Trigger する(入力イベントのTriggerdを呼ぶ)かという処理が実装されたクラスです。
デフォルトでは以下のようなものが用意されています。
Down … 押している間、常にTriggerする
Hold … 設定時間以上押したらTriggerする
Pressed … 押した時に一度だけTriggerする
Released … 話した時にTriggerする。
Pulse … 押している間、一定周期でTriggerする
カーソル移動に使用する Down と Up の InputAction には Triggers に Pressed を設定しています。
これでは星座のように大量に選択肢があるメニューの場合、十字キーを無駄に連打させる必要があります。
プレイヤーを腱鞘炎にさせないためにも、カーソル君には長押しで高速移動してもらいたいです。
C++で以下のようなコードを追加します。
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 |
#pragma once #include "CoreMinimal.h" #include "InputTriggers.h" #include "BlogEIInputTriggerHoldInterval.generated.h" UCLASS(Abstract, Config = Input) class UBlogEIInputTriggerTimedBase : public UInputTrigger { GENERATED_BODY() protected: UPROPERTY(BlueprintReadWrite, Category = "Trigger Settings") float HeldDuration = 0.0f; virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override; public: virtual FString GetDebugState() const override { return HeldDuration ? FString::Printf(TEXT("Held:%.2f"), HeldDuration) : FString(); } }; UCLASS(NotBlueprintable, meta = (DisplayName = "Hold And Accelerate")) class UBlogEIInputTriggerHoldAndAccelaerate : public UBlogEIInputTriggerTimedBase { GENERATED_BODY() bool bTriggered = false; float LastTriggerElapsedTime = 0.0f; protected: virtual ETriggerState UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) override; public: //ホールド時間閾値 UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") float HoldTimeThreshold = 0.5f; //最大インターバル時間 UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") float MaxTriggerIntervalTime = 0.25f; //最小インターバル時間 UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") float MinTriggerIntervalTime = 0.03f; //加速時間 UPROPERTY(EditAnywhere, Config, BlueprintReadWrite, Category = "Trigger Settings") float AccelerationTime = 2.0f; }; |
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 |
#include "BlogEIInputTriggerHoldInterval.h" #include "Kismet/KismetMathLibrary.h" ETriggerState UBlogEIInputTriggerTimedBase::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) { ETriggerState State = ETriggerState::None; // Transition to Ongoing on actuation. if (IsActuated(ModifiedValue)) { State = ETriggerState::Ongoing; HeldDuration += DeltaTime; // TODO: When attached directly to an Action this will tick N times a frame where N is the number of evaluated (actively held) mappings. } else { // Reset duration HeldDuration = 0.0f; } return State; } ETriggerState UBlogEIInputTriggerHoldAndAccelaerate::UpdateState_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue ModifiedValue, float DeltaTime) { // UBlogEIInputTriggerTimedBaseでHeldDurationを更新 ETriggerState State = Super::UpdateState_Implementation(PlayerInput, ModifiedValue, DeltaTime); //経過時間から現在のインターバル時間を決定する float Alpha = FMath::Clamp(HeldDuration - HoldTimeThreshold, 0, AccelerationTime) / AccelerationTime; float TriggerInterval = FMath::Lerp(MaxTriggerIntervalTime, MinTriggerIntervalTime, Alpha); //押している時間がThresholdを超えていて、間隔が設定以上ならTrigger bTriggered = (HeldDuration >= HoldTimeThreshold) && (LastTriggerElapsedTime >= TriggerInterval); if (bTriggered) { //経過時間をリセット LastTriggerElapsedTime = 0.0f; return ETriggerState::Triggered; } LastTriggerElapsedTime = LastTriggerElapsedTime + DeltaTime; return State; } |
これで新たにHold And Accelerateという、
「一定時間押した後に、だんだん早くなる周期でTriggerする」InputTriggerが作成されます。
なお、UBlogEIInputTriggerTimedBase については、
InputTriggers.h に UInputTriggerTimedBase という同様の処理をするクラスが実装されていますが、
MinimalAPIで実装されており継承ができないため、新たに実装しています。
新たに作成した Hold And Accelerate を Down と Up の InputAction に追加登録します。
これでパッドの上下キーを長押しすると、
カーソルが高速移動するようになりました!
ハクスラとかで100個単位で装備を選択するなどのシチュエーションがあるゲームには必須ですね!
EnhancedInputは古代の谷やLyla Starter Gameでも使用されており、
UE5ではある程度スタンダードな機能になっていくのではないかなと予想しています。
拡張することでUIの独特な入力にも対応できそうなので、使ってみてはいかがでしょうか?
UE5のUIの入力周りは CommonUI も気になるところですが…