K.Y.さん、しんちょくどーですか?

仕事や趣味で知ったこと、つまずいたことを書いています

【UE4】Widgetでモバイルのタッチ入力を検出する関数(関数オーバーライド)の紹介

まえがき

モバイル向けにタッチ入力を検出するイベントが用意されている(↓参考)のですが、Widgetから利用することはできません。 yashinut.com

どうにかWidgetでタッチ入力を検出できないか調べたところ、関数オーバーライドを利用するとよいことが分かったので使い方を紹介します。

確認環境

Widgetの関数をオーバーライドする方法

まず、タッチ入力を検出するためにはWidgetで関数をオーバーライドする必要があります。WidgetのBPで関数の横にある+マークにマウスをホバーさせると「オーバーライド」というプルダウンが出てくるので、そこからオーバーライドしたい(利用したい)関数を選びます。

+マーク関数オーバーライド
↑関数横の+マーク | 関数オーバーライド一覧↑

ここで選択した関数は特定の処理が発生するとUE4のシステムから呼び出されるようになっています。そして呼び出された時の処理を関数内に書くことになります。

お試しオーバーライド
試しにOn Touch Started関数をオーバーライド

↑ではOn Touch Started関数をオーバーライドしました。この関数は指がWidgetに触れたときに呼び出されます。(タッチ系の各関数は後で触れていきます)

また、画像を見てわかるように内部の処理を書いただけだとリターンノードで警告がでてしまいます。(コンパイル時に発生します)
これを修正するには出力ピンにHandledノードかUnhandledノードをつないでやる必要があります。どちらをつなぐかは基本的に↓の考えでOKです。

  • 関数内で処理を行った場合 -> Handledノードをつなぐ
  • 関数内で何もしていない場合 -> Unhandledノードをつなぐ

警告回避
警告回避!

タッチ入力に関係する関数一覧

↓に挙げる関数をオーバーライドしておくと特定のタッチ入力があったときに呼び出されます。関数名を見ればどんな入力があったときに呼び出されるのか想像がつく人もいると思います。

  • On Touch Gesture関数
  • On Touch Started関数
  • On Touch Moved関数
  • On Touch Ended関数
  • On Touch Force Changed関数 <- 使い方を知らないので紹介を省きます
  • On Mouse Enter関数
  • On Mouse Leave関数

後半にOn Mouse Enter関数、On Mouse Leave関数が入っているのですが、これは書き間違いではありません。タッチ入力を検出したいのでOn Touch XXX関数だけ使うのかと思いきや、一部のOn Mouse XXX関数も関係してきます。
自分はいろいろ試しているうちにこれに気づきましたが危うく見逃すところでした...。

以降はここに挙げた関数をまとめて「タッチ系関数」と表記します。

タッチ系関数の紹介...の前に

タッチ系関数を使う際、いくつか注意点があるので紹介しておきます。

PCでタッチ入力をテストする方法

タッチはモバイル端末でテストするのが一番ですが、サクッと動作を確認したい場合はPIEを利用することになると思います。その場合PCでタッチ入力をシミュレートするために、マウス入力をタッチ入力として扱う設定が必要なります。

設定は↓をオンにするだけでOKです。
プロジェクト設定 > エンジン > インプット > マウスプロパティ > Use Mouse for Touch

タッチ入力設定
マウス入力をタッチ入力として扱う設定

タッチ系関数が反応する条件

タッチ系関数を反応させるには下記の条件が必要になります。

  • Widget内にあるUIパーツのVisibilityがVisibleである

この条件を満たしてタッチ系関数をオーバーライドしないと、タッチ入力に反応しません。
自分は最初UIパーツのVisibilityをNot Hit - Testable(タッチに反応しない設定)にしたまま配置していたので、タッチ入力が取れずにつまずいていました...。

また、ButtonやSliderなど一部のUIパーツはタッチ入力を横取りされるので注意が必要です。なのでImageをVisibleにして配置するのが無難かなと思います。

配置
ButtonとImageを配置

↑の場合、Image(白色のところ)の上をタッチした時だけタッチ系関数が反応します。ButtonとImageが重なっているところをタッチすると、Buttonが手前にあるためタッチ入力を横取りされます。
逆に考えると、UIパーツの前後関係でタッチ反応範囲を制限できるので何か使いようがあるかもしれませんね。

タッチ系関数の紹介

On Touch Gesture関数

関数オーバーライド

ジェスチャー入力がされたときに呼び出される関数です。

ジェスチャー入力ということは、フリックやピンチ操作も検出できるのか!...と期待してしまうのですが、実はこれ内部実装がほとんどされていません。 詳しくは下記ツイートのスレッドに書いています。(あくまで私個人の推察ではありますが)

どうしてもジェスチャー入力を検出したいという人は、下に挙げた方法で我慢しましょう。

自分でもタッチ系関数を組み合わせてジェスチャー入力の検出に挑戦しました。対応したジェスチャーの種類は少ないですが、いつか記事にして実装例を公開できれば...と思っています。
todo:記事書いたらリンク張る [20/24/2/28 追記] それから時は流れ、UE5時代に突入してしまいました。UE5で再検証するのに腰が上がらず結局記事をかけそうにないです...すみません。

On Touch Started関数

関数オーバーライド

指がWidgetを押したときに呼び出される関数です。
画面上ではなくWidgetのUIパーツ上を押したときに呼び出されることに注意してください。

反応範囲
UIパーツ上を押したときに反応

On Touch Moved関数

関数オーバーライド

指がWidget上で動いたときに呼び出される関数です。これを使うとスワイプ操作を検出できます。
On Touch Started関数と同様、画面上ではなくWidgetのUIパーツ上で指が動いたときだけ呼び出されることに注意してください。

On Touch Ended関数

関数オーバーライド

指がWidgetから離れたときに呼び出される関数です。
On Touch Started関数と同様、画面上ではなくWidgetのUIパーツ上で離されたときに呼び出されることに注意してください。

On Mouse Enter関数

関数オーバーライド

指がWidgetの内側へ入ったときに呼び出される関数です。(リターンノードが用意されていないので、イベントとして出現します)

On Mouse Leave関数

関数オーバーライド

指がWidgetの外側へ出たときに呼び出される関数です。(リターンノードが用意されていないので、イベントとして出現します)
On Mouse Enter関数の逆ですね。

On Mouse Enter関数とOn Mouse Leave関数が呼び出されるタイミングのイメージは↓になります。

関数のイメージ画像
関数呼び出しのイメージ

タッチ系関数内で取得できる情報の紹介

各関数が呼び出されたとき同時にタッチに関する情報が送られてきます。取得すると便利そうなものに絞って紹介します。他にもあるので、興味のある人はいろいろ探してみてください。

タッチした指の番号

Get Pointer Indexノードで取得できます。

1番目にタッチした指は0、2番目にタッチした指は1...という感じで値が割り振られています。
これをTouch Indexとして扱いたい場合は何段階か変換を挟む必要があります。

変換工程
整数値からETouchIndex型に変換する工程

また、PCでテストする際は注意点がありまして「マウスポインタも指」として扱わることがあります。これはOn Mouse Enter関数とOn Mouse Leave関数で発生することを確認しています。
指番号としては10が割り当てられていますので、指番号で処理を分岐する際は気を付けてください。

マウスポインタは指番号10として扱われる

タッチした座標(ビューポート上)

Get Screen Space PositionノードとAbsolute to Viewportノードを組み合わせて取得できます。

このノードで「ビューポート上の座標」を取得できるのですが、ビューポート上の座標とは画面左上からの座標のことです。座標のイメージは↓になります。

座標イメージ
ビューポート上の座標

タッチした座標(Widget上)

Get Screen Space PositionノードとAbsolute to Localノードを組み合わせて取得できます。
(ただ、On Mouse Leave関数については送られてくる情報が足りないので使うことができません)

こちらのノードで取得できるのは「Widget上の座標」です。ちょっと伝わりづらいかもしれませんが、座標のイメージは↓になります。

座標イメージ
Widget上の座標

スクショではWidget_Test1にタッチした座標を取得するよう仕込んであります。そして、Widget_Test1の範囲は緑枠線の部分なのでWidget上の座標とはこの枠線左上からの座標となります。

スワイプ時の移動量

Get Cursor Deltaノードで取得できます。

結構便利そうなのですが、試してみたところ精度が粗いです。感覚としては小数点以下が切り捨てられているようでした。それでもOKという人はこのノードを使いましょう。

より正確な移動量を取りたい場合は自前で計算するほうがよさげです。実装としてはスワイプ時に前回タッチした座標と今回タッチした座標の差分をとればいいかなと思います。

実装例
移動量を計算する実装例

タッチ系関数をオーバーライドして使ってみる

この記事のおさらいの意味も込めて、Widgetでタッチ入力を検出する実装例を書いておきます。ここまで読んで手元で試してみたけどタッチ検出できなかった...という人は、ここに書いた手順通りに進めてみてください。

  1. (PCでテストしたい人)マウス入力をタッチ入力として扱う設定をオンにする
    タッチ入力設定

  2. Widgetを作成し、Imageを配置

  3. 配置したImageのVisibilityがVisibleになっているかを確認

  4. 使いたいタッチ系関数をオーバーライド
    関数オーバーライド

  5. 任意のレベルを作成し、自作したWidgetを表示するようノードを組む

  6. ゲームを実行して動作確認!

参考資料

あとがき

タッチ系関数でOn Touch Gesture関数が使えないのは残念...。でも、指が押された、動いた、離されたことさえ検出できればジェスチャー入力の疑似実装はできるので、まぁいいかなとも思ったり。

あとジェスチャー入力を自前で作っているときにOn Touch XXX関数だけを使っていると、Widgetの外側で指が離された場合にOn Touch Ended関数で検出ができなくてすごく困ることがありました。が、On Mouse Leave関数で対処できることを発見したときは感動しましたね。関数名だけ見てタッチに使えそうなものを選んでいたので盲点でした。