
RPGのコマンドメニューやタイトル画面のように、マウスを使わずにキーボードの矢印キーやゲームパッドの十字キーで項目を選んで、決定ボタンで決める。コンシューマー機やSteamのゲームでは当たり前の操作ですが、UnityのUIは最初のままだと、矢印キーを押しても何も起きません。今回は、UIの「選択」の仕組みとNavigationの設定、そしてスクリプトで選択を動かす方法を、RPGのコマンドメニューを作りながらご紹介します!
この記事で学べること
- UIの「選択」とは何か、キーボードとゲームパッドのどのボタンで動くのか
- EventSystemの「First Selected」で、最初に選ぶボタンを決める方法
- 選んでいるボタンを目立たせる「Selected Color」
- Navigationで、カーソルが動く方向を決める方法
- 選ばれたときに▶のカーソルを出す方法(ISelectHandler)
- スクリプトで選択を動かして、サブメニューを開け閉めする方法
前提知識
- UnityのUI(Canvas)システムを知ろう:EventSystemの役割
- テキスト、ボタン、画像の基本UI要素:ButtonとonClick
- Scroll ViewとLayout Groupでリストを作ろう:Vertical Layout Group
- UIをドラッグ&ドロップしよう:マウスのイベントを受け取る:インターフェースとCanvas Group
- Input Systemで入力を受け取ろう:InputSystem_Actions
プロジェクトの確認
- Unityのバージョンは6.3 LTS
- テンプレートはUniversal 2D
- Input Systemを使う設定(Unity 6の新しいプロジェクトは最初からこの設定です)
- EventSystemには「Input System UI Input Module」が付いている
UIの「選択」ってどんなもの?
マウスで操作するときは、押したいボタンを直接クリックしますよね。キーボードやゲームパッドには、画面の場所を指すものがありません。そこでUIには、「今どのボタンを選んでいるか」が常に1つだけある、という仕組みがあります。
- 矢印キーや十字キーで、選んでいるボタンを上下左右に動かす
- 決定キーで、選んでいるボタンを押す(クリックしたのと同じ)
- 今どれを選んでいるかは、EventSystemが覚えている
どのキーで動くかは、InputSystem_ActionsのUIのAction Mapで決まっています。最初の割り当ては次のとおりです(Unity 6.3で確認)。
| 操作 | Action | キーボード | ゲームパッド |
|---|---|---|---|
| 選択を動かす | Navigate | 矢印キー・WASD | 十字キー・左右のスティック |
| 決定 | Submit | Enter | 下のボタン(Xboxの「A」、PlayStationの「×」) |
| キャンセル | Cancel | Esc | 右のボタン(Xboxの「B」、PlayStationの「○」) |
コマンドメニューを作ろう
まずは、RPGのバトルによくある「たたかう・まほう・どうぐ・にげる」のメニューを作ります。
- Hierarchyで右クリック>UI (Canvas)>Panel で「CommandPanel」を作る
- Canvasの左下に置き、Width:360、Height:400ぐらい
- Add Component>Vertical Layout Group を付けて、Padding:各20、Spacing:12、Control Child SizeのWidth・Heightにチェック
- CommandPanelの子に、右クリック>UI (Canvas)>Button – TextMeshPro で4つのボタンを作る
- 名前は「AttackButton」「MagicButton」「ItemButton」「RunButton」
- 中の文字は「たたかう」「まほう」「どうぐ」「にげる」(日本語のフォントは9-3の記事)
- 画面の上の方に、UI (Canvas)>Text – TextMeshPro で「MessageText」を作る(選んだコマンドを表示するのに使います)

キーボードで動かしてみよう!
この状態で再生して、↓キーを押してみてください。・・・何も起きませんね。
これは、今どのボタンも選ばれていないからです。選んでいるものが無いので、動かす相手がいません。(試しにボタンを1つクリックしてから↓キーを押すと、動くはずです。クリックすると、そのボタンが選ばれるからです。)
First Selectedで、最初に選ぶボタンを決めよう
- HierarchyでEventSystemを選ぶ
- InspectorのEvent Systemの「First Selected」に、AttackButtonをドラッグする

再生すると、最初からたたかうが選ばれた状態になり、↓キーで下に、↑キーで上に動くようになります。Enterキーを押すと、選んでいるボタンが押されます(onClickが呼ばれます)。
選んでいるボタンを目立たせよう
動くようにはなりましたが、今どれを選んでいるのか、ほとんど見分けがつきません。ButtonのSelected Color(選ばれているときの色)が、最初は白に近い色(245, 245, 245)になっているからです。
- Hierarchyで4つのボタンをまとめて選ぶ(Ctrlを押しながらクリック)
- InspectorのButtonの「Selected Color」を、目立つ色(黄色など)にする
- ついでに「Highlighted Color」(マウスを乗せたときの色)も同じ色にしておくと、マウスとキーボードで見た目がそろいます


Selected(選ばれている)とHighlighted(マウスが乗っている)は別の状態です。キーボードやゲームパッドで操作するゲームでは、Selected Colorを必ず目立つ色にしておきましょう。
Navigationで、動く方向を決めよう
矢印キーを押したときに「次にどのボタンへ動くか」を決めているのが、ButtonのNavigationです。
| Navigation | 動き方 |
|---|---|
| Automatic(最初の設定) | 画面の位置を見て、押した方向にある一番近いボタンへ動く |
| Vertical | 上下のキーだけで動く |
| Horizontal | 左右のキーだけで動く |
| Explicit | 上下左右それぞれ、どのボタンへ動くかを自分で指定する |
| None | キーでは動かない(このボタンには止まらない) |
InspectorのNavigationの下にある「Visualize」ボタンを押すと、Sceneビューに、どのボタンからどのボタンへ動くかが矢印で表示されます。

スライムVisualizeの矢印、小さくて見えにくいです・・・Sceneビューをぐっと拡大すると見やすくなります
一番下から一番上に戻れるようにしよう
Automaticのままだと、にげるで↓キーを押しても、それ以上は動きません。RPGのメニューでは、一番下で↓を押したら一番上に戻ると便利ですよね。
- 4つのボタンをまとめて選び、Navigationを「Vertical」にする
- その下に出てくる「Wrap Around」にチェックを入れる


これで、にげるで↓を押すとたたかうへ、たたかうで↑を押すとにげるへ、ぐるっと回るようになります。
使えないボタンは飛ばされる
アイテムを持っていないときの「どうぐ」のように、今は選べないボタンもあります。
- ItemButtonのButtonの「Interactable」のチェックを外す
Interactableがオフのボタンは、灰色になって、Navigationでも飛ばされます。まほうで↓を押すと、どうぐを飛ばしてにげるへ動きます。
▶のカーソルを付けよう
色だけでなく、RPGらしく選んでいる項目の左に▶を出してみましょう。ボタンが選ばれた・外れたは、9-7で使ったのと同じイベントのインターフェースで受け取れます。
- 各ボタンの子に、UI (Canvas)>Text – TextMeshPro で「Cursor」を作り、文字を「▶」にして、ボタンの左はしに置く
- 「MenuCursor」というスクリプトを作り、次のように書く
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
// 選ばれているあいだだけ、▶のカーソルを出す
public class MenuCursor : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler
{
[SerializeField] private GameObject cursor;
private Selectable selectable;
private void Awake()
{
selectable = GetComponent<Selectable>();
cursor.SetActive(false);
}
private void OnDisable()
{
// 選ばれたまま隠れたときに、▶が残らないようにする
cursor.SetActive(false);
}
// 選ばれたとき(キーボード・ゲームパッド・クリック)
public void OnSelect(BaseEventData eventData)
{
cursor.SetActive(true);
}
// 選択が外れたとき
public void OnDeselect(BaseEventData eventData)
{
cursor.SetActive(false);
}
// マウスを乗せたら、そのボタンを選ぶ(マウスとキーボードで、▶の場所をそろえる)
public void OnPointerEnter(PointerEventData eventData)
{
if (selectable.IsInteractable())
{
EventSystem.current.SetSelectedGameObject(gameObject);
}
}
}
- ISelectHandler / IDeselectHandler
OnSelectは選ばれたとき、OnDeselectは選択が外れたときに呼ばれる。キーボードでもゲームパッドでもクリックでも、選ばれ方に関係なく呼ばれる
- IPointerEnterHandler
- マウスを乗せたときに、そのボタンを選ぶ。こうしておくと、マウスで乗せた項目とキーで選んでいる項目が、いつも同じになる
- Interactableがオフのボタンは選ばない(
IsInteractable())
- OnDisable
- メニューごと隠したとき、
OnDeselectは呼ばれない。▶が次に開いたときに残らないよう、ここで消しておく
- メニューごと隠したとき、
4つのボタンにMenuCursorを付けて、それぞれのCursorに、自分の子のCursorをドラッグします。





4つのボタンに1つずつ付けて、1つずつドラッグするの、大変です・・・



1つのボタンを作り込んでからPrefabにすると楽ちんだよ!ボタンが増えるゲームなら、最初からPrefabにしておくのがオススメ
スクリプトで選択を動かそう
First Selectedが効くのは、ゲームが始まったときの1回だけです。サブメニューを開いたとき・閉じたときのように、あとから選択を動かしたいときは、スクリプトで選びます。
EventSystem.current.SetSelectedGameObject(選びたいボタンのgameObject);
メニューを閉じて開き直したときや、パネルを切り替えたときは、First Selectedは効きません。選択していたボタンが隠れると、何も選ばれていない状態になって操作できなくなるので、開いたらスクリプトで選び直しましょう。
「まほう」を選ぶと右にまほうの一覧が開き、キャンセルで閉じて「まほう」に戻る、という動きを作ってみましょう。
- CommandPanelをコピーして「MagicPanel」にし、CommandPanelの右に置く
- 中のボタンを3つにして、名前を「FireButton」「HealButton」「BackButton」、文字を「ファイア」「ヒール」「もどる」にする
- ボタンのMenuCursorはそのまま使える(コピーした分は、Cursorの欄が自分の子を指しているか確認)
- CommandPanelにAdd Component>Canvas Group を付ける
- 空のGameObject「CommandMenu」を作る
「CommandMenu」というスクリプトを作り、次のように書きます。
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
// コマンドメニューの選択と、まほうのメニューの開け閉め
public class CommandMenu : MonoBehaviour
{
[SerializeField] private CanvasGroup commandGroup;
[SerializeField] private Button attackButton;
[SerializeField] private Button magicButton;
[SerializeField] private Button runButton;
[SerializeField] private GameObject magicPanel;
[SerializeField] private Button fireButton;
[SerializeField] private Button healButton;
[SerializeField] private Button backButton;
[SerializeField] private TMP_Text messageText;
private InputSystem_Actions inputActions;
private GameObject lastSelected;
private void Awake()
{
inputActions = new InputSystem_Actions();
attackButton.onClick.AddListener(() => ShowMessage("たたかう を えらんだ!"));
magicButton.onClick.AddListener(OpenMagic);
runButton.onClick.AddListener(() => ShowMessage("にげる を えらんだ!"));
fireButton.onClick.AddListener(() => ShowMessage("ファイア を となえた!"));
healButton.onClick.AddListener(() => ShowMessage("ヒール を となえた!"));
backButton.onClick.AddListener(CloseMagic);
}
private void OnEnable()
{
inputActions.Enable();
}
private void OnDisable()
{
inputActions.Disable();
}
private void Start()
{
magicPanel.SetActive(false);
Select(attackButton);
}
private void Update()
{
// キャンセル(Esc・ゲームパッドの右のボタン)で、まほうのメニューを閉じる
if (inputActions.UI.Cancel.WasPressedThisFrame() && magicPanel.activeSelf)
{
CloseMagic();
}
// 何もないところをクリックして選択が外れたら、前に選んでいたボタンを選び直す
GameObject current = EventSystem.current.currentSelectedGameObject;
if (current == null)
{
if (lastSelected != null && lastSelected.activeInHierarchy)
{
EventSystem.current.SetSelectedGameObject(lastSelected);
}
}
else
{
lastSelected = current;
}
}
// まほうのメニューを開いて、ファイアを選ぶ
private void OpenMagic()
{
commandGroup.interactable = false;
magicPanel.SetActive(true);
Select(fireButton);
ShowMessage("どの まほうを つかう?");
}
// まほうのメニューを閉じて、まほうのボタンに戻る
private void CloseMagic()
{
magicPanel.SetActive(false);
commandGroup.interactable = true;
Select(magicButton);
ShowMessage("");
}
private void Select(Selectable target)
{
EventSystem.current.SetSelectedGameObject(target.gameObject);
}
private void ShowMessage(string message)
{
messageText.text = message;
}
}
- EventSystem.current.SetSelectedGameObject(…)
- 選択を、そのボタンに動かす。前に選ばれていたボタンには
OnDeselect、新しいボタンにはOnSelectが届く
- 選択を、そのボタンに動かす。前に選ばれていたボタンには
- commandGroup.interactable = false;
- Canvas GroupのInteractableをオフにすると、その中のボタンが全部オフになる。まほうのメニューを開いている間に、カーソルがコマンドの方へ動いてしまわないようにする
- inputActions.UI.Cancel.WasPressedThisFrame()
- キャンセルのAction(Esc・ゲームパッドの右のボタン)が押されたか。6-7と同じ書き方
- BackButton
- マウスだけで遊ぶ人のために、キャンセルと同じことをするボタンも用意しておく
- lastSelected
- マウスで何もないところをクリックすると、選択が外れて、キーボードで動かせなくなります。外れていたら、前に選んでいたボタンを選び直す
サブメニューを開いたら中の最初のボタンを、閉じたら開く前のボタンを選ぶ。この2つを守るだけで、キーボードでもゲームパッドでも迷子にならないメニューになります。
Canvas GroupのInteractableは、子のボタン全部のInteractableとかけ合わせて効きます。どうぐのようにもともとオフのボタンは、Canvas Groupをオンに戻してもオフのままです。
CommandMenuにスクリプトを付け、それぞれの欄に、CommandPanel(Canvas Group)・各ボタン・MagicPanel・MessageTextをドラッグします。スクリプトで最初のボタンを選ぶので、EventSystemのFirst Selectedは空に戻してOKです。


動作確認!
再生して、キーボードで操作してみましょう。


- 再生すると、最初から「たたかう」に▶が付いている
- ↓キーで下に動き、「どうぐ」は飛ばされる。「にげる」で↓を押すと「たたかう」に戻る
- 「まほう」でEnterを押すと、まほうのメニューが開いて「ファイア」が選ばれる。左のコマンドは灰色になる
- Escキーか「もどる」で閉じると、▶が「まほう」に戻る
- マウスを乗せたボタンに▶が動く。何もないところをクリックしても、▶は消えない


よくあるつまずき
矢印キーを押しても何も選ばれない
- 原因:どのボタンも選ばれていない
- 対処法:EventSystemのFirst Selectedを設定するか、スクリプトで
SetSelectedGameObjectを呼ぶ
選んでいるボタンが分からない
- 原因:Selected Colorが最初の白っぽい色のまま
- 対処法:Selected Colorを目立つ色にする。▶のような目印を出すのもオススメ
マウスでクリックしたら、キーボードで動かなくなった
- 原因:何もないところをクリックすると、選択が外れる
- 対処法:選択が外れたら、前のボタンを選び直す(CommandMenuの
lastSelected)
カーソルが思わぬボタンへ飛ぶ
- 原因:NavigationがAutomaticで、画面の位置から近いボタンを選んでいる
- 対処法:メニューごとにVerticalやHorizontalにする。複雑な並びならExplicitで1つずつ指定する。Visualizeで矢印を確かめる
Spaceキーで決定できない
- 原因:最初の割り当てでは、決定(Submit)はEnterだけ
- 対処法:InputSystem_ActionsのUIのSubmitに、Keyboardのspaceを足す
まとめ
- キーボードやゲームパッドで操作するには、「今どのボタンを選んでいるか」が要る。最初の1つはFirst Selected、あとからは
SetSelectedGameObjectで選ぶ - 動かすのはNavigate(矢印キー・十字キー)、決定はSubmit(Enter・下のボタン)、キャンセルはCancel(Esc・右のボタン)
- Selected Colorは必ず目立つ色に
- Navigationで動く方向を決める。VerticalとWrap Aroundで、上下にぐるっと回るメニューになる
ISelectHandler・IDeselectHandlerで、選ばれたときの見た目(▶)を作れる
今回のコマンドメニューは、ターン制RPGの作り方シリーズで、バトルのコマンドとして使っていく予定です!
コメント