- UE5 Common UI お試し ① ボタンのアイコンを出し分ける - You are done!
- UE5 Common UI お試し ② デフォルトナビゲーション (Default Click Action) - You are done!
上記の続きです。
前回デフォルトナビゲーションのうち、Default Click Action しか触れられなかったので Default Back Action を試します。
設定に関するコード
前回も貼りましたが再掲します。
/* Derive from this class to store the Input data. It is referenced in the Common Input Settings, found in the project settings UI. */ UCLASS(Abstract, Blueprintable, ClassGroup = Input, meta = (Category = "Common Input")) class COMMONINPUT_API UCommonUIInputData : public UObject { GENERATED_BODY() public: virtual bool NeedsLoadForServer() const override; public: UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (RowType = CommonInputActionDataBase)) FDataTableRowHandle DefaultClickAction; UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (RowType = CommonInputActionDataBase)) FDataTableRowHandle DefaultBackAction; };
また、これらを取得するコードは UCommonInputSettings
にあります。
UCLASS(config = Game, defaultconfig) class COMMONINPUT_API UCommonInputSettings : public UDeveloperSettings { FDataTableRowHandle GetDefaultClickAction() const; FDataTableRowHandle GetDefaultBackAction() const; // 全体的に省略
GetDefaultBackAction 利用箇所
void UCommonActivatableWidget::NativeConstruct() { Super::NativeConstruct(); if (bIsBackHandler) { // 💡 Default Back Action 時に HandleBackAction が呼び出される FBindUIActionArgs BindArgs(ICommonInputModule::GetSettings().GetDefaultBackAction(), FSimpleDelegate::CreateUObject(this, &UCommonActivatableWidget::HandleBackAction)); BindArgs.bDisplayInActionBar = bIsBackActionDisplayedInActionBar; DefaultBackActionHandle = RegisterUIActionBinding(BindArgs); } if (bAutoActivate) { UE_LOG(LogCommonUI, Verbose, TEXT("[%s] auto-activated"), *GetName()); ActivateWidget(); } }
上記の一箇所しかありません。
UCommonActivatableWidget
は、最初の記事で WBP_Menu
を作成する際に UserWidget
の代わりに親に指定したものです。
NativeConstruct
で呼び出されていますが、bIsBackHandler
の場合のみのようです。
上記のプロパティになりますが、もちろん WBP_Menu
にも存在しています。以下は定義です。
/** * The base for widgets that are capable of being "activated" and "deactivated" during their lifetime without being otherwise modified or destroyed. * * This is generally desired for one or more of the following purposes: * - This widget can turn on/off without being removed from the hierarchy (or otherwise reconstructing the underlying SWidgets), so Construct/Destruct are insufficient * - 💡 You'd like to be able to "go back" from this widget, whether that means back a breadcrumb, closing a modal, or something else. This is built-in here. * - This widget's place in the hierarchy is such that it defines a meaningful node-point in the tree of activatable widgets through which input is routed to all widgets. * * By default, an activatable widget: * - Is not automatically activated upon construction * - 💡 Does not register to receive back actions (or any other actions, for that matter) * - 💡 If classified as a back handler, is automatically deactivated (but not destroyed) when it receives a back action * * Note that removing an activatable widget from the UI (i.e. triggering Destruct()) will always deactivate it, even if the UWidget is not destroyed. * Re-constructing the underlying SWidget will only result in re-activation if auto-activate is enabled. * * TODO: ADD MORE INFO ON INPUTS */ UCLASS(meta = (DisableNativeTick)) class COMMONUI_API UCommonActivatableWidget : public UCommonUserWidget { // 略 /** True to receive "Back" actions automatically. Custom back handler behavior can be provided, default is to deactivate. */ UPROPERTY(EditAnywhere, Category = Back) bool bIsBackHandler = false; /** True to receive "Back" actions automatically. Custom back handler behavior can be provided, default is to deactivate. */ UPROPERTY(EditAnywhere, Category = Back) bool bIsBackActionDisplayedInActionBar = false; /** Handle to default back action, if bound */ FUIActionBindingHandle DefaultBackActionHandle;
上記でクラスのコメントに強調アイコンを付けましたが、UCommonActivatableWidget
としても back に関しては主要な機能として説明されています。
戻ると、bIsBackHandler
は Editor から設定可能な bool
値になっています。
bIsBackActionDisplayedInActionBar
に関しては今回はスキップします。
DefaultBackActionHandle
に関しては RegisterUIActionBinding
の戻り値を受け取り、Deactivate
のために利用しているだけです。実際のアクションの登録は RegisterUIActionBinding
で行われています。
Back Action を受け取った時
すでに定義コードのコメントで書かれていますが、以下の実際の処理コードでも分かる通り DeactivateWidget
が呼び出されています。(BP 側で OnHandleBackAction
が設定されている場合は呼び出されないようです)
bool UCommonActivatableWidget::NativeOnHandleBackAction() { //@todo DanH: This isn't actually fleshed out enough - we need to figure out whether we want to let back be a normal action or a special one routed through all activatables that lets them conditionally handle it if (bIsBackHandler) { if (!BP_OnHandleBackAction()) { // Default behavior is unconditional deactivation UE_LOG(LogCommonUI, Verbose, TEXT("[%s] handled back with default implementation. Deactivating immediately."), *GetName()); DeactivateWidget(); } return true; } return false; } void UCommonActivatableWidget::HandleBackAction() { NativeOnHandleBackAction(); }
DeactivateWidget
動作を確認するにあたり、CommonActivatableWidget
の Deactivate がどのようなことをするのか見ておきます。
void UCommonActivatableWidget::DeactivateWidget() { if (bIsActive) { InternalProcessDeactivation(); } } void UCommonActivatableWidget::InternalProcessDeactivation() { UE_LOG(LogCommonUI, Verbose, TEXT("[%s] -> Deactivated"), *GetName()); bIsActive = false; NativeOnDeactivated(); } void UCommonActivatableWidget::NativeOnDeactivated() { if (ensure(!bIsActive)) { if (bSetVisibilityOnDeactivated) { SetVisibility(DeactivatedVisibility); UE_LOG(LogCommonUI, Verbose, TEXT("[%s] set visibility to [%d] on deactivation"), *GetName(), *StaticEnum<ESlateVisibility>()->GetDisplayValueAsText(DeactivatedVisibility).ToString()); } // Cancel any holds that were active ClearActiveHoldInputs(); BP_OnDeactivated(); OnDeactivated().Broadcast(); BP_OnWidgetDeactivated.Broadcast(); } }
まずは bIsActive
を false
にし、その後 bSetVisibilityOnDeactivated
が true
なら SetVisibility(DeactivatedVisibility)
を行います。
ClearActiveHoldInputs
に関しては HoldInputs
をまだ確認していないので詳しくは見ませんが、それをクリアするということかと思います。
その後は BP 側のイベントを流したり、OnDeactivated
を流したりといった感じです。
bSetVisibilityOnDeactivated / DeactivatedVisibility
// UCommonActivatableWidget.h UPROPERTY(EditAnywhere, Category = Activation, meta = (InlineEditConditionToggle = "DeactivatedVisibility")) bool bSetVisibilityOnDeactivated = false; UPROPERTY(EditAnywhere, Category = Activation, meta = (EditCondition = "bSetVisibilityOnDeactivated")) ESlateVisibility DeactivatedVisibility = ESlateVisibility::Collapsed;
こちらも設定項目ですね。IsBackHandler
を確認する時にも同じ箇所のスクショを貼りましたが、今回は上記のプロパティの箇所に枠を付けています。
ひとまず Hidden
することにして消してしまいます。
実行する
ひとまずこれまでに作った NavigationBack
を指定します。
何も起きない?
消えませんでした。この Widget 内部で NavigationBack
のイベントを受け取るボタンが存在しているのでそこでクリックが消費されると Back までいかないのかもしれません。
異なるボタンを割り当てる
上記のようにここまで利用していないボタンを割り当てました。
すると、Keyborad、Gamepad 両方のボタンで消えることが確認できました。
まとめ
Default Click Action はボタンのホバー時にアイコンを表示するだけだったのに対して Default Back Action はアイコン表示はなく、入力イベントを待ち受け Deacitivate を行うというものでした。
ゲームのユーザーとしては使うボタンは少ない方がいいので、こういう default で指定された入力をうまく使って UI も設計できればいいのだろうと思いました。