はじめに
Canvasにボタンを実装するだけでも、色々方法があるのでざっとまとめてみました。
Unityバージョン2018.4.91で動作確認済みです。
シンプルなボタンの実装
とりあえず一番簡単なボタンの実装を見ていきます。HierarceyウィンドウからCreate>UI>Canvasを選択します。Canvasゲームオブジェクトが作成されますが、この時EventSystemゲームオブジェクトも一緒にシーンに追加されます。
作成したCanvasゲームオブジェクトのRender ModeをScreen Space – Cameraに変更し、Render CamerにMain Cameraを選択します。続いて、Canvasの子として、Hierarceyウィンドウから Create>UI>Buttonを選択します。下記のスクリプトを作成し、Buttonオブジェクトに配置します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using UnityEngine; using UnityEngine.UI; public class ButtonTest : MonoBehaviour { private Text _buttonText; private void Start() { _buttonText = GetComponentInChildren<Text>(); } public void ChangeText() { _buttonText.text = "押されたよ"; } } |
最後に、ButtonコンポーネントのOn ClickにButton Testをドラッグして割り当て、Click時に呼ばれス関数として ButtonTest.ChangeTextを選択します。
プレイモードでボタンを押下すれば、文字が変わります。
ボタンに必要な要素
上で見たボタンは、雛形が用意してあるので簡単でしたが、ボタンとして機能させるにはいくつかのオブジェクトが相互に作用しあっています。大事なところを見ていきます。
Event Systemオブジェクト
Canvasの作成時に一緒に作成されるゲームオブジェクトですが、ユーザーの入力を受け取り、イベントに応じてUIオブジェクトに設定されたイベントハンドラを発火する役割を持っています。
Graphic Raycasterコンポーネント
Canvas作成時に一緒に付与されるコンポーネントです。
Canvasに対してのみ追加可能なコンポーネントで、Canvasに対してRayを放ちGraphicオブジェクトを検知する役割を持っています。
Graphic オブジェクト
Graphic Raycasterの検知対象になるオブジェクト群です。Graphicクラスを継承している ImageコンポーネントやTextコンポーネントなどが該当します。
イベントハンドラのインターフェース
イベントハンドラは 何らかのイベントが発生したときに起動されるものを指します。クリックした時、ドラッグした時などのイベント発火時によばれるメソッドを定義したインターフェースです。ButtonコンポーネントはIPointerClickHandlerインターフェースを実装しており、クリック時にOnPointerClickメソッドが呼ばれています。
ボタン実行までの流れ
大雑把に、ボタン実行の流れを説明すると下記になると思います。まず、EventSystemがユーザーの入力イベントを受け取り、Canvas上でRayを放ちます。RayがGraphicオブジェクトを検知した場合、オブジェクトに イベントハンドラのインターフェース を実装している場合は、それを呼び出します。 (*イベントハンドラ が実装されていない場合、親ゲームオブジェクトの イベントハンドラを再帰的に探し、そのイベントハンドラを実行します)
ボタン実装色々
ここまでの説明を踏まえ、色々見ていきます。
ボタンを押した瞬間に処理を実行する
Buttonコンポーネントは、 IPointerClickHandlerインターフェースのみ継承しているため、クリックのみ検知できます。クリックは、押して離すの一連の動作のため、実際にイベントが発火するのは、離したタイミングです。押した瞬間のイベントハンドラは、IPointerClickHandler インターフェース で実装できます。 IPointerClickHandlerを継承したスクリプトを Graphicオブジェクトに追加してもよいですが、Event Triggerコンポーネントを追加し、Pointer Downを選ぶことでも実装できます。
Event Triggerコンポーネントは、 Pointer Down 以外にも様々なイベントハンドラを持っているのでざっと確認しておくと良いと思います。
3Dオブジェクトをボタンにする
3DキューブをCanvasに配置し、イベントハンドラを追加してもイベントは実行されません。Graphicオブジェクトではないからです。
CameraにPhysics Raycasterをつければ、3Dオブジェクトに対して衝突を検知し同様にイベントを実行可能ですが、Graphic Raycasterだけで対応する方法も考えてみます。手っ取り早そうなのは、透明なImageオブジェクトを用意して、子を3Dオブジェクトとすることでしょうか。
イベントを一か所で処理する。
Graphicオブジェクトにイベントハンドラを個別に設定する場合が多いと思いますが、イベントハンドラを一か所で処理するパターンも紹介しておきます。
Canvasオブジェクトに対して、イベントハンドラを設定することで 一か所でイベントを処理できます。これは、 Graphicオブジェクトにイベントハンドラがない場合、親を辿りCanvasオブジェクトのイベントハンドラが呼ばれるためです。
検知済みのGraphicオブジェクトは、イベントハンドラ関数の第一引数であるeventDataに含まれているので、そこから処理分けできます。参考にOnPointerDownのコードを張っておきます。
1 2 3 4 5 6 7 |
public void OnPointerDown(PointerEventData eventData) { if (eventData.pointerCurrentRaycast.gameObject.CompareTag("Card")) { // doSomething }; } |
UniRXで書く
UniRXの記法でも、イベント処理が書けます。
Graphicオブジェクトに、Observable Pointer Down Triggerコンポーネントを追加します。
あとは、 Observable Pointer Down Triggerをスクリプト側で操作すればよいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using UniRx; using UniRx.Triggers; using UnityEngine; public class ButtonTest : MonoBehaviour { private void Start() { GetComponent<ObservablePointerDownTrigger>().OnPointerDownAsObservable() .Subscribe(eventData => { //do something } ); } } |