最近Unityにはまってゲームを作っている。 ゲームを作っていると、選択肢を表示させてプレイヤーに選ばせたい場面がでてくる。例えば、レベルアップ時にスキルを複数個提示してどれか1つだけ選択できるみたいな。ノベルゲーならプレイヤーの行動や言動を選択するみたいなやつ。 これを最初は自分でロジックを書いて実装していたのだが、マウス入力に対応させようとするとどうすればいいかわからなかった。ネットを検索すると色々方法論はでてくるのだが、古い情報だったり大掛かりな実装だったりするが、うまく手元では動かなくてどうしたものかとなっていた。 しかし、色々試していたらInput System UI Input Moduleでかなり簡単に実装できたのでメモしておく。
試した環境
- Unity 6000.0.30f1
- Input System 1.11.2
作るもの
今回つくるUIはこちら
キーボードのW/Sキーで上下で選択肢の変更を行い、その後マウスのクリックでも行っている。 肝心の実装方法だが、Input System UI Input Moduleを使う。こいつはEventSystemオブジェクトを作ると自動で追加されるコンポーネントである。
具体的な役割はキーボードやゲームパッドでUIを操作するための便利コンポーネントという感じ(だとおもう)。 UI サポート | Input System | 1.4.3 当初何に使うかどのように動作するのか全くわからなかったが、こいつは指定されたInput SystemのActionを受け取って、Selectableなオブジェクトを操作するという動作する。 このSelectableなオブジェクトを対象としているというのが全くわからずハマった…*1。 つまるところ、選択肢として表示したいオブジェクトにSelectableコンポーネントを追加すればよい。 UI.Selectable - Unity スクリプトリファレンス ちなみに、ButtonなどのいくつかのUIオブジェクトはSelectableになっている*2(重要)ので配置するだけで、Input System UI Input Moduleで選択できるようになる。
実際に作成手順を見ていく。
オブジェクトの準備と設定
ここでは例として、Canvasの配下にImageオブジェクト3つとそれぞれその配下にText Mesh Proなオブジェクトをぶら下げるといった構成にした。また、EventSystemも自動で追加されるものを使用している。
ここではInput System UI Input Moduleの設定はデフォルトのままにしている。操作するキーを変える場合は、MoveやSubmitのアクションを適宜変更すれば良い。
次に選択肢として使用するオブジェクトにSelectableコンポーネントを追加していく。ここではButton1/Button2/Button3に追加する。追加の仕方は簡単で対象のオブジェクトをHierarchyから選択し、Inspectorからコンポーネントの追加 -> Selectableを選択すればよい。
Selectableコンポーネントの設定はデフォルトのままでもよいが、どのオブジェクトが選択されているかをわかりやすくするためSelected Colorを橙色(#FFB600)に変更した。
これで完成である。簡単!
マウスホバーで選択肢を変更する
前述したとおりマウスの場合、クリックしないと選択肢を変更できない。 ゲームによってはマウスホバーで選択肢を変更できるようにしたい場合がある。 これを手っ取り早く実装するにはEvent Triggerコンポーネントを利用する。 具体的には、対象のオブジェクトのInspectorを開きコンポーネントの追加 -> Event Triggerを選択する。
Event Triggerを追加したら、Event Triggerの設定から新しいイベントタイプを追加を選択し、Pointer Enterを選択する。
追加されたPointer Enterのリストの+を押してイベントを追加する。
次にRuntime Onlyと表示されているセレクトボックス下のGameObjectの選択ボックスにEvent Systemオブジェクトをドロップする。
そして、No Functionsとかかれたセレクトボックスを選択し、Event System -> SetSelectedGameObject(GameObject)を選択する。
最後に、EventSystem.SetSelectedGameObjectの下のボックスに自分の(重要)オブジェクトをドロップする(下の画像の例ではButton1)。
これでマウスホバーで選択肢を変更できるようになっているはずである。
設定内容の解説
設定した内容を解説すると、Event Triggerコンポーネントは様々なイベントを受け取って任意のオブジェクトの関数を実行してくる機能を提供している。これを利用して、マウスがオブジェクトの範囲内に入ったら(Pointer Enter)、Event SystemのSetSelectedGameObjectを呼び出して自分自身を選択させている。 Seletableなオブジェクトであれば、OnSeleteを読んで選択させることもできるが、これだと他のオブジェクトが選択されている場合に選択を解除できないので、複数選択させたくない場合は別途仕組みを用意必要がある。
選択肢を決定したときの処理を追加する
Event SystemのSubmitアクションを設定している場合、そのアクションに沿った操作をすると選択されているオブジェクトに対してSubmitイベントが発生する。 このSubmitイベントを受けるためにオブジェクトに対してMonoBehaviorスクリプトを追加してOnSubmitを定義してもいいが、前述したEventTriggerを利用して親オブジェクトに通知すると処理をまとめることができて見通しがよくなる。
ここでは、Canvasオブジェクトに以下のようなMonoBehaviroスクリプトをUIEventという名前で追加した。
using UnityEngine; using UnityEngine.EventSystems; public class UIEvent : MonoBehaviour { public void OnSubmit() { // 選択されているオブジェクトを取得 GameObject selectedObject = EventSystem.current.currentSelectedGameObject; // どのボタンが選択されているかを判定 GameObject button1 = GameObject.Find("Button1"); if (selectedObject == button1) { Debug.Log("Button1が選択されています"); } GameObject button2 = GameObject.Find("Button2"); if (selectedObject == button2) { Debug.Log("Button2が選択されています"); } GameObject button3 = GameObject.Find("Button3"); if (selectedObject == button3) { Debug.Log("Button3が選択されています"); } } }
現在選択されているオブジェクトは EventSystem.current.currentSelectedGameObject
で取得できる。
これで取得できるGameObjectとSelectableなオブジェクトを比較するとよい。
OnSubmitはEvent Triggerからキックするためにpublicに設定する必要があるので注意。
スクリプトを設定したら、マウスホバーのときと同様の手順でEvent TriggerにSubmitを追加する。このとき、送信先をCanvasオブジェクトにして、FunctionをOnSubmitにする。
ちなみにデフォルトではUI/Submitに設定されたInputActionにはマウスの左クリックが含まれていない。 そのため、マウスの左クリックで選択できるようにするには、独自のInputActionを設定する必要がある(もしくはPointerClickイベントをトリガーしてもいいかもしれない)。
Tips: シーン開始時に最初に選択されているオブジェクトを指定する
EventSystemの最初の選択にオブジェクトを指定すると、シーン開始時にそのオブジェクトが選択された状態にすることができる。
Tips: 意図しないオブジェクトが選択される(選択が消える)
ボタンを3つしか配置していないの、WやSキーを押していると選択が消える…ということがおこる。 これの原因は、Selectableなオブジェクトが配置されているからである。どういうこと?っと思うかもしれないが、ButtonやSliderも実はSelectableなオブジェクトである。これらを選択させないようにするにはInteractableをオフにする必要がある。
Tips: 複数シーンにおけるEventSystemの切替
UIの話とは直接関係ないが…ゲーム画面とUI画面をわけてマルチシーンにすることがある。 そのとき、EventSystemが複数あるとWARNINGが発生してしまう。
There are 2 event systems in the scene. Please ensure there is always exactly one event system in the scene UnityEngine.EventSystems.EventSystem:Update () (at ./Library/PackageCache/com.unity.ugui/Runtime/UGUI/EventSystem/EventSystem.cs:543)
これを回避するには、少し面倒くさいがEventSystemを手動で切り替える必要がある。 EventSystemの切り替えはEventSystem.currentにEventSystemのインスタンスを代入するとよい。
// SceneA側はSceneBをロードする前にEventSystemを無効化しておく EventSystem ev = EventSystem.current ev.enabled = false; SceneManager.LoadScene("SceneB"); (SceneBの処理) // SceneBの処理が終わったらEventSystemも有効にする ev.enabled = true; EventSystem.current = ev; //---------------------------------------------------- // SceneB側はロードされたら自分のEventSystemを登録する // ここではStartでやる [Serialized] GameObject eventSystem; void Start() { EventSystem.current = eventSystem.GetComponent<EventSystem>(); (SceneBの処理) // 処理が終わったら無効化しておく EventSystem.current.enabled = false }