【Unity】UIのボタンを使わずにクリックを検知する方法

【Unity】UIのボタンを使わずにクリックを検知する方法

Buttonコンポーネントを使えばいいんじゃね?

の1行で全否定されそうな記事ではありますが、例えばボタンとして意識させたくないけど、クリックしたりタップしたりすると反応するキャラクターなどを導入したい場合には使える方法です。

CameraにPhysics Raycasterを付ければUIだけでなく3Dオブジェクトをクリックしたことも検知してくれるので便利。

 

環境

macOS 10.13 High Sierra

Unity2018.1.0f2

まずはButtonの動きから

オーソドックスにButtonコンポーネントを使ってクリックを検知してみましょ。Canvasの子要素として[UI] -> [Button]からButtonオブジェクトを作成します。デフォルトの状態に、クリックされた時に呼び出すスクリプトをセットしました。

いつものButtonコンポーネント
いつものButtonコンポーネント

 

スクリプトはこんな感じ。クリックされたらコンソールにデバッグ文を出力します。

Buttonオブジェクトは画像のようにシンプルに。

まずはUIのボタンから
まずはUIのボタンから

 

ゲームを実行して上の[Button]をクリックすると、メッセージが表示されました。

ちゃんとメッセージが表示された
ちゃんとメッセージが表示された

 

ここまではOK。今度は、Buttonコンポーネントを使わずにクリックを検知してみます。

EventSystemを使おう

Unityでクリックやドラッグ&ドロップを検知する時には、EventSystemが使われます。

スクリプトから使う場合には、UnityEngine.EventSystemsをusingで宣言します。

これに加えてインターフェースを実装します。使うのはIPointerClickHandler。このインターフェースは画面がクリックされた時に発行されるOnPointerClickのイベントを拾いたい時に実装します。

UIがクリックされた時にはこれだけで拾ってくれますが、3DのオブジェクトなどはカメラにPhysics Raycasterコンポーネントをアタッチする必要があります。というのは、マウスやポインターの位置はスクリーン座標で表され、3Dオブジェクトなどはワールド座標で表されるため。

座標系が違うので、単純にスクリーン上の位置を拾うだけではオブジェクトをクリックしたことになっているかまで分からないのです。

なので、スクリーン上の位置からRay(光線)を飛ばして、オブジェクトがあることを確認しています。

詳細についてはUnityのスクリプトリファレンスもご覧あれ。

 

使い方詳細

上でも少し触れましたが、3Dオブジェクトの場合とUIの場合では若干手順が異なります。

3Dオブジェクトの場合

ワールド座標で表される3Dオブジェクトなどの場合は、以下の手順でクリックイベントを拾うことができます。

  1. カメラにPhysics Raycasterコンポーネントをアタッチする
  2. クリックイベントを拾うスクリプトで『usingUnityEngine.EventSystems』を宣言
  3. インターフェースとして『IPointerClickHandler』を実装
  4. OnPointerClick(PointerEventData pointerData)で処理を拾う

カメラにPhysics Raycasterコンポーネントをアタッチするのさえ忘れなければ、そんなに難しくはないと思います。インターフェースってなんぞ? って人はグーグル先生にお尋ねするとよろし。いつかこのブログでも分かりやすく扱いたいなぁ(願望)

サンプルスクリプトはこちら。

UIのボタンが押された時のように、3Dオブジェクトがクリックされた時にコンソールへ出力を行います。

3Dオブジェクトのクリックを検知
3Dオブジェクトのクリックを検知

 

デバッグ文だけだと寂しいので、3Dオブジェクトをクリックしたら色が変わるようにしてみましょうか。上のスクリプトをちょっといじります。

3Dオブジェクトの色を変えたいので、マテリアルを取得してその色を変えています。

コルーチンでは指定した時間の間、色が遷移するようにしています。initColorで黄色、targetColorで緑色を指定し、1秒かけて黄色から緑色に変化します。

経過時間に応じて、Color.Lerp(線形補間)を使って現在の色を取得し、マテリアルにセットしています。

コルーチンの中身の処理はテラシュールブログさんのこちらの記事を参考にしています。

 

ゲームを実行してオブジェクトをクリックするとこんな感じ。クリックした時に3Dオブジェクトの色が黄色になり、だんだんと緑色に変わっていきます。

クリックイベントを拾ってくれてる
クリックイベントを拾ってくれてる

 

UIの場合

スクリーン座標で表されるUIの場合は、以下の手順でクリックイベントを拾うことができます。

  1. クリックイベントを拾うスクリプトで『usingUnityEngine.EventSystems』を宣言
  2. インターフェースとして『IPointerClickHandler』を実装
  3. OnPointerClick(PointerEventData pointerData)で処理を拾う

カメラにPhysics Raycasterコンポーネントをアタッチする必要がない点で3Dオブジェクトの場合と異なります。逆に言えば違いはそこだけ。

Imageでクリックを拾うのだポッター
Imageでクリックを拾うのだポッター

 

スクリプトは3Dオブジェクトの場合と似ていますが、Imageコンポーネントを使っているので、ダイレクトに色を変化させます。

変更点はMaterialの色をセットしていたのをImageに変えただけ。UIでMaterialをいじらないのは、CanvasのRender Modeを『Screen Space – Overlay』にしているためです。画面の一番手前に描画するので、光が当たらないから真っ暗になっちゃうんですよね。

このスクリプトをImageコンポーネントを持つオブジェクトにアタッチして実行します。3Dオブジェクトの場合と同じように、クリックすると色が変わります。

UIでもいける
UIでもいける

 

Buttonとの使い分け

使い分けを考えたときに最初に浮かぶのは、3Dオブジェクトでクリックイベントを拾いたい場合でしょうか。

敵がドロップしたアイテムやお金をクリックして拾う、なんて時に位置やオブジェクトを特定するのに便利。

あとは今回のようにクリックするとオブジェクトの色が変わったり、なんなら電気のスイッチをクリックすると部屋が明るくなったりなんてのもいいですね。

3Dゲームだと、UIを通してじゃなくてマウスやタップで直接オブジェクトを触って反応させたい場面も多々あるので、そうした時にこのIPointerClickHandlerを実装するといいかも。

UIを使うんだったらButtonを使うのが無難です。というかButtonコンポーネント自体もIPointerClickHandlerを実装してるのよね。

あえてButtonコンポーネントを使わない場面となると……ノベルゲームなどで、立ち絵をクリックすると反応するけど、Buttonコンポーネントのように状態によって色を変えたくない、なんて時は自分のスクリプトにIPointerClickHandlerを実装する方向で。

Buttonコンポーネントで状態ごとの色をセットするのはめんどくさい、でもクリックは拾いたい、みたいな。ドラッグ&ドロップのイベントも拾いたいなーって時には自分のスクリプトにハンドラを入れていくのがいいかも。

EventSystemの確認は忘れずに

自分でUI要素を作成した時には自動的に作られますが、Prefabなどをインスタンス化してSceneを組み立てる場合は、『EventSystem』オブジェクトがいることを確認してね。

これがないとイベント拾ってくれないんです。クリックも、ドラッグ&ドロップも、『EventSystem』オブジェクトがいないとダメなんです。

もしScene内になかったら、右クリックして[UI] -> [EventSystem]で作れます。

「あれ!? ボタンを押しても全然反応しないよ!?」

って時はだいたいこの『EventSystem』オブジェクトがいない時。何度引っかかったことか……。

アセット作ってます!

CTA-IMAGE

Unityでの開発に役立つアセットを作っています。

3DダンジョンRPGを開発するスピードを200%加速するAssetや、ファンタジーRPGのダンジョンを彩るパーツを取り揃えています。

特に3DダンジョンRPGのゲームを1から作るのは結構時間がかかります。ダンジョン部分の作成はこうしたアセットを使って、開発をブーストさせてみませんか?