前回、Common Button Base を使用して、Common Action Widget のホールド表現を行った。
が、Common Button Base はあくまでボタンのとしての機能を提供するものなので、HUD に表示したいだけの用途としては片手落ち感があった。
今回は、独自に C++ で HUD 表示向けのホールド表現が可能な Widget を CommonUI を使って自作してみる。
実装した内容
結論から。基本的には以下の問題を解決する実装を行うことができた。
- CommonButtonBase にしか入力イベントのバインドが実装されていないため、HUD 表示だけしたい場合に面倒なことになる問題
- → 内部に Button クラスを保持せず、入力イベントのみ接続するシンプルな Widget を新規に実装
 
 - ホールドの進捗が完了したときに、Click イベントが呼ばれる
- → シンプルに Complete イベントとして呼ぶように変更
 
 - ホールド時間が InputAction の設定固定であり、状況に応じた上書きができない
- → Widget 上から上書きできるような Override 設定を追加
- (ただし、この実装は Private なヘッダーファイルの参照が必要だったので利用は自己責任)
 
 
 - → Widget 上から上書きできるような Override 設定を追加
 
実装過程
せっかくなので今回作ったもののコードは参考用として github にプラグインとしてあげておいた。
 (たぶん、保守はしないと思う・・・)
Dependency Module の設定
今回は CommonUI のプラグインの UCommonUserWidget クラスを継承して作成する。
結果的には、ビルドするには以下のモジュールが必要だった。(GameplayTags は正直どこに作用)
- CommonUI
 - UMG
 - CommonInput
 - GameplayTags
 
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				// ... add other public dependencies that you statically link with here ...
				"CommonUI",
			}
			);			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				// ... add private dependencies that you statically link with here ...	
				"UMG",
				"GameplayTags",
				"CommonInput",
			}
			);今回はプラグイン化しちゃったので、プラグインの Build.cs に記述しているが、直接プロジェクトにソース追加する場合は、プロジェクト側の Build.cs に似たような Module の追加が必要になる。
UInputBindingActionWidget
UCommonUserWidget を継承して、UCommonAcitonWidget を取り扱う UInputBindingActionWidget を作成する。
基本的には、UCommonButtonBase から、ボタン機能由来の余計な操作をすべて外しつつ、ホールド機能だけそのまま持ってきているだけ。
以下、需要なメソッドだけ解説。
BindTriggeringInputAction メソッド
このクラスにおいて最も重要な CommonUI 管理の入力イベントへのバインドは、 BindTriggeringIntputAction メソッドでおこなっている。
以下のように、UCommonUserWidget::RegisterUIActionBinding メソッドを使用すると、入力イベントに対してバインドが可能。
void UInputBindingActionWidget::BindTriggeringInputAction()
{
	if (TriggeringInputAction.IsNull())
	{
		return;
	}
	if (!TriggeringBindingHandle.IsValid())
	{
		FBindUIActionArgs bindUIActionArgs(TriggeringInputAction, false, 
									FSimpleDelegate::CreateUObject(this, &UInputBindingActionWidget::NativeOnActionComplete));
		bindUIActionArgs.OnHoldActionProgressed.BindUObject(this, &UInputBindingActionWidget::NativeOnActionProgress);
		bindUIActionArgs.bIsPersistent = bIsPersistentBinding;
		bindUIActionArgs.InputMode = InputModeOverride;
		TriggeringBindingHandle = RegisterUIActionBinding(bindUIActionArgs);
	}
}ここで、FBindUIActionArgs は、RegisterUIActionBinding に必要な引数で、Delegate の設定などをおこなうことができる。
今回の場合は、
- 完了イベント: NativeOnActionComplete
- BP 上では OnActionComplete イベント
 
 - ホールド中の Progress イベント: NativeOnActionProgress
- BP 上では OnActionProgress イベント
 
 
というようにそれぞれイベントを登録している。
bIsPersistent は前回も解説したように、入力ルーティングを無視した永続化されたバインド設定を意味する。
InputMode に関しては、現状の実装だと、C++ で UCommonActivatableWidget を継承して使用しない限りは無用なオプション。LyraStarterGame サンプルで一部使用されている。
である。
UpdateInputActionWidgetVisibility メソッド
UCommonButtonBase では、キーボード入力だった場合に非表示にするオプション(bHideInputActionWithKeyboard) があったため同じように実装しておく。
void UInputBindingActionWidget::UpdateInputActionWidgetVisibility()
{
	if (InputActionWidget)
	{
		bool bHidden = false;
		auto* CommonInputSubsystem = GetInputSubsystem();
		if (CommonInputSubsystem != nullptr) 
		{
			bHidden = bHideInputActionWithKeyboard && CommonInputSubsystem->GetCurrentInputType() != ECommonInputType::Gamepad;
		}
		InputActionWidget->SetHidden(bHidden);
	}
}UCommonUserWidget を継承していると、CommonInputSubsystem から入力の状態を簡単に知ることができる。
HoldTime を強制的に上書きするオプション (Private ヘッダー使用)
HoldTime を動的に変更するには、CommonUI の Private フォルダ以下にある Input/UIActionRouterTypes.h にアクセスする必要がある。
通常、Private フォルダ以下のヘッダーファイルはインクルード不可だが、Build.cs に小細工をすれば無理やりできないことはない。
		// HoldTime の上書き実装を行うため、
		// 無理やり CommonUI の Private ヘッダーを参照可能にする設定
		PublicDefinitions.Add("COMMON_UI_PRIVATE_ACCESS");
		if (PublicDefinitions.Contains("COMMON_UI_PRIVATE_ACCESS"))
        {
			string engine_path = Path.GetFullPath(Target.RelativeEnginePath);
			string common_ui_path = engine_path + "Plugins/Experimental/CommonUI/Source/CommonUI/";
			PrivateIncludePaths.Add(common_ui_path + "Private");
		}念のため、”COMMON_UI_PRIVATE_ACCESS” という名前で define を設定した。PublicDefinitions.Add("COMMON_UI_PRIVATE_ACCESS"); 自体をコメントアウトしてビルドすると、この機能自体を無効化できるようにしておく。
UInputBindingActionWidget では、以下のコードで HoldTime の上書きを行っている。
FUIActionBinding::FindBinding でハンドルからバインドの実体を取得して、ホールドのマッピング情報を無理やり上書きする。
void UInputBindingActionWidget::OverwriteHoldTime()
{
#ifdef COMMON_UI_PRIVATE_ACCESS
	if (TriggeringBindingHandle.IsValid())
	{
		if (auto binding = FUIActionBinding::FindBinding(TriggeringBindingHandle))
		{
			for (FUIActionKeyMapping& holdMapping : binding->HoldMappings)
			{
				holdMapping.HoldTime = HoldTimeOverride;
			}
		}
	}
#endif // COMMON_UI_PRIVATE_ACCESS
}HoldTime の変更インタフェースとして、プロパティから Hold Time Override で設定、または、Set Hold Time のノードを用意してみる。
なお、Set Hold Time に関しては、変更時にホールド中の場合だった場合には失敗扱いとした。
サンプルアセット
今回作った UInputBindingActionWidget の利用例として、UInputBindingActionWidget を継承したクラスを2つ用意しておく。
完成
できたー。
Set Hold Time ノードの確認も兼ねて Hold Time を変更しつつ確認できるようにした。
X, Y を同時押しているように、Y のホールド時間が上書きされてることがわかる。
 






コメント