LoginSignup
7
7

More than 3 years have passed since last update.

[UE4] フォーカスとUIナビゲーション

Last updated at Posted at 2020-06-03

対象Ver:UE4.25.0

1. フォーカス

 Widgetにはフォーカスが可能かどうかを指定するフラグUUserWidget::bIsFocusableがあります。フォーカスが当たっているウィジェットはインタラクションが可能になり、例えばButtonにフォーカスが当たっている場合はスペースキーを押すとボタンのPressを実行し、テキストボックスにフォーカスされている場合は文字を入力することが可能です。

2020-06-02_22h13_24.png

 UserWidgetの場合、このフラグに応じてフォーカス可能かの判定UUserWidget::NativeSupportsKeyboardFocusを行いますが、SObjectWidget::SupportsKeyboardFocusの実装を見るとButtonやListViewなどといった特定のWidgetへのフォーカス可否については、独自の実装SObjectWidget::SupportsKeyboardFocusによって決まっています。Button, CheckBox, Slider, ListViewなどは、ユーザー操作によってフォーカスの移動が可能であるものであるため、有効になっています。

2. フォーカスの判定

 キーボードやコントローラなどの入力によりフォーカスを適用するかは最初にFSlateApplication::AttemptNavigationを実行します。ここでTabやShift+Tabなど「フォーカス送り」の処理はFWeakWidgetPath::ToNextFocusedPathで実行し、割り当てられたインデックス番号を元に次の番号か前の番号のWidgetにフォーカスを移動します。最終的にフォーカスの移動はFWidgetPath::MoveFocusで行います。
video_01.gif

図:1,2,3,4の順に生成されたら1→2→3→4の順にタブ送りされる

 一方、キー操作など「フォーカスのフリー移動」処理はFHittestGrid::FindFocusableWidgetで行います。こちらの方法では、入力に応じて視覚的に上下右左のいずれかのWidgetにフォーカスをうつすことをします。最終的なフォーカスの送り先はWidgetの位置や大きさなどから判断して移動しますが、移動できないようなケースも存在します。
video_01.gif
図:キー操作の方向にあるUIが次の移動となる

 例えば下の二つの図は、2→4のボタンにフォーカスを移動させたいとした場合、2のボタンにフォーカスが当たっている状態でキーボードの右を押下した時にフォーカスが移動できるか?というものです。左は高さの部分が重複しているのでフォーカスの移動ができます。右の図では高さが不一致なので右を押してもフォーカスは移動しません。フォーカスが移動できるのはその方向にあるWidgetの領域が交差する場合です。

2020-06-02_22h15_17.png  2020-06-02_22h17_00.png
※左:2→4にフォーカス移動可能、右:2→4にフォーカス移動不可

3. UIナビゲーション

 AIのナビゲーション機能と名前が被るので明示的に示すためにUIナビゲーションとしています。上記でフォーカスが移動する/しないという話をしましたが、ユーザーの操作によってUIの選択位置が移動するのがナビゲーションの機能です。以下のブログでは詳しく説明されています。

ヒストリア様ブログ:UMGのNavigation機能を使ってみる

4. ナビゲーション操作

2020-06-02_22h39_08.png

項目 概要
Escape 操作方向/Index指定にフォーカスは移動します
Stop 操作方向/Index指定への移動を抑制します
Wrap ループさせたい時などに逆方向に移動します
Explicit 指定のWidgetにフォーカスを移動します
Custom 指定のデリゲート関数を呼び出します
CustomBoundary 境界線判定移動+指定のデリゲート関数を呼び出します

 Escapeは"逃亡, 脱出"という意味を持つように、現在のフォーカスから脱出して指定された方向に移動します。デフォルトではこの設定によってカーソル, フォーカスの移動ができるようになっています。
video_01(2).gif

 Stopは"停止, 中断"という意味を持つように、現在のフォーカスから脱出することを抑制します。つまりキーが押されたとしてもカーソルやフォーカスは移動しません。それ以上移動させたくない時などに利用します。

 Wrapは"包括"の意味を持ちますが、反対側に移動する際に利用します。また、これは子widgetに対して適用されるので、同一レイヤにあるサンプルのボタンに対しては適用されません。以下の例では親widget(HorizontalBox)内のコンテンツに対して適用されるWrapの例です。カーソルが端のコンテンツに当たってない場合はLeft/Right共に順番に移動します。①の左端にカーソルがある場合、LeftにWrapで設定していると入力後は反対の②に移ります。同様に②の右端にカーソルがある場合、RightにWrapで設定していると入力後は反対の①に移ります。
wrap.png

 Explicitは指定するWidgetにフォーカスを移動させます。例えば以下の例では、右が押された時に順番や方向の移動を無視して"Button_10"に強制的にカーソルを移動させることができます。ただし、この機能には現在不具合があるのでこの修正を適用する必要があります。
2020-06-03_15h28_55.png

CustomはWrapと同様、Custom/Custom Boundaryは子widgetに対して適用されるので、同一レイヤにあるサンプルのボタンに対しては適用されません。カーソル移動の境界線判定を行わずにデリゲート関数を呼び出します。①にカーソルがある時、Leftを入力するとCustomに登録されたデリゲート関数は呼び出されます。

Custom Boundaryはカーソル移動の境界線判定を行ってから移動デリゲート関数を呼び出します。
①にカーソルがある時、Rightを入力すると境界線判定を行って移動可能なためカーソルを移動してデリゲート関数は呼び出されません。右端に到達した時、Rightを入力すると境界線判定を行って移動できるジオメトリが無い場合はデリゲート関数を呼び出します。
custom.png

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7