【Unity】ボタンの作り方|クリック処理・シーン移動・押せない時の対処

タイトル画面に「スタート」を置いて、押したらゲームが始まる。Unityのボタンでやることは、突き詰めるとこの「置く」と「押したら何かを呼ぶ」の2つだけです。この記事では、Unity 6でボタンを作る手順、クリックで関数を呼ぶ2つの方法、シーン移動、見た目の変え方、そして「押しても反応しない」時に見る場所までを順番にまとめました。

手順はUnity 6.3(6000.3.14f1)に入っているuGUI 2.0.0のメニュー名と初期値で確認しています。UI Toolkitではなく、昔からあるuGUI(Canvasを使うUI)の話です。

先に結論を書いておくと、ボタン作りの流れは次の3つです。

1. GameObject > UI (Canvas) > Button – TextMeshProでボタンを置く
2. publicな関数を書いたスクリプトを用意し、On Clickの欄に登録する(またはAddListenerで登録)
3.押せない時は、EventSystemの有無と「上に透明なUIが重なっていないか」をまず見る

スポンサーリンク

Unity 6でのボタンの作り方(配置まで)

まずはシーンにボタンを1つ置くところから。Unity 6.3では、UIのメニューの名前が少し変わっています。

メニューからButton – TextMeshProを置く

ヒエラルキーの空いている所を右クリックし、UI (Canvas) > Button – TextMeshProを選びます。上のメニューバーならGameObject > UI (Canvas) > Button – TextMeshProです。Unity 6.0〜6.2や2022では「UI > Button – TextMeshPro」という名前なので、古い解説の画面と違っても慌てなくて大丈夫ですね。

シーンにCanvasがなければ、ボタンと一緒にCanvasとEventSystemが自動で作られます。ヒエラルキーはこうなるはずです。

Canvas
  └ Button
      └ Text (TMP)
EventSystem

初めてTextMeshProを使うプロジェクトだと「TMP Importer」という小さな画面が出ます。Import TMP Essentialsを押してください。これを飛ばすと文字が表示されません。あとから入れる時はWindow > TextMeshPro > Import TMP Essential Resourcesです。

メニューにはLegacy > Buttonもありますが、こちらは古いTextを使う版。新しく作るならTextMeshProの方を選んでおくのが無難だと思います。

大きさ・位置・文字を整える

ボタンの大きさと位置は、ButtonのRect TransformのWidth・Height・Pos X・Pos Yで決めます。画面の右下に固定したい時は、Rect Transformの左上にある四角(アンカーのプリセット)で右下を選んでから位置を合わせると、解像度が変わってもずれにくくなります。

表示する文字は、子のText (TMP)を選んでインスペクターのText Inputを書き換えます。日本語を入れると□(豆腐)になることがありますが、これは標準のフォントアセットに日本語の文字が入っていないため。日本語フォントからFont Assetを作る必要があり、Window > TextMeshPro > Font Asset Creatorで作れます。

ちなみにボタンの見た目の画像は、ButtonについているImageコンポーネントのSource Imageです。ここを差し替えれば自前の画像のボタンになります(後半で詳しく書きます)。

ボタンを押したら処理を呼ぶ:On ClickとAddListener

ボタンは押しただけでは何もしません。押されたら呼ぶ関数を、ButtonコンポーネントのonClickに登録します。やり方はインスペクターで登録する方法と、スクリプトで登録する方法の2つです。

インスペクターのOn Clickに登録する

まず呼びたい関数を書いたスクリプトを用意します。関数はpublicで、引数なし(または引数1つ)にしてください。privateだと一覧に出てきません。

using UnityEngine;

public class StartButton : MonoBehaviour
{
    // インスペクターの On Click から呼ぶ関数(public で、引数なし)
    public void OnClickStart()
    {
        Debug.Log("スタートボタンが押されました");
    }
}

続いて登録の手順です。

1.空のGameObjectを作って(名前は「UIManager」など)、StartButtonを付ける
2. Buttonを選び、インスペクターの一番下にあるOn Click ()の「+」を押す
3.「None (Object)」の欄に、手順1のUIManagerをヒエラルキーからドラッグする
4.右の「No Function」からStartButton > OnClickStart ()を選ぶ

ここでつまずきやすいのが手順3。スクリプトのファイル(プロジェクトウィンドウの.cs)を入れても関数は選べません。入れるのは、そのスクリプトが付いているシーン上のオブジェクトです。

再生してボタンを押し、Consoleに「スタートボタンが押されました」と出れば成功ですね。

On Clickの左上にある「Runtime Only」は、再生中だけ呼ぶという意味の初期値です。普段は触らなくて構いません。

引数を1つ渡す(int・float・string・bool・Object)

On Clickから呼ぶ関数には、引数を1つだけ渡せます。対応している型はint・float・string・bool、それとObject(GameObjectやスプライトなど)です。引数が2つ以上ある関数や、Vector3を受け取る関数は一覧に出てきません。

「どのボタンを押したかで処理を分けたい」なら、同じ関数をintの引数付きにして、ボタンごとに0、1、2と数字を入れておく使い方ができます。次のシーン移動の例も、この仕組みを使っています。

スクリプトのAddListenerで登録する

ボタンが多い時や、実行中に作ったボタンに処理を付けたい時は、スクリプトから登録する方が管理しやすいです。Button.onClick.AddListenerに関数を渡します。

using UnityEngine;
using UnityEngine.UI;

public class ButtonListener : MonoBehaviour
{
    [SerializeField] private Button startButton;   // インスペクターでボタンをドラッグして入れる
    [SerializeField] private Button optionButton;

    private void OnEnable()
    {
        // 押されたときに呼ぶ関数を登録する
        startButton.onClick.AddListener(OnStart);
        // 1行で済む処理ならラムダ式でも書ける
        optionButton.onClick.AddListener(() => Debug.Log("オプションを開きます"));
    }

    private void OnDisable()
    {
        // 登録した関数は外しておく(二重登録を防ぐ)
        startButton.onClick.RemoveListener(OnStart);
        optionButton.onClick.RemoveAllListeners();
    }

    private void OnStart()
    {
        Debug.Log("ゲームを始めます");
    }
}

スクリプトで登録した関数は、インスペクターのOn Clickの欄には表示されません。「何も登録されていないのに動く」と後で混乱しがちなので、どちらの方法で登録したかはチームや未来の自分のためにコメントで残しておくと安心です。

もう1つの注意は、同じ関数を2回AddListenerすると、1回押しただけで2回呼ばれること。OnEnableで登録したらOnDisableで外す、と対にしておけば防げます。RemoveAllListenersはスクリプトで足した分だけを外し、インスペクターで登録した分は残ります。

for文でボタンを並べて番号を渡す書き方は、ノベルゲームの選択肢の記事で実例を載せています。

ボタンを押したらシーン移動する

タイトルからゲーム本編へ、ゲームオーバーからリトライへ。ボタンでいちばんよく使うのがシーンの切り替えでしょう。SceneManager.LoadSceneを呼ぶ関数を作り、引数のシーン名をOn Clickの欄で入れる形にすると、1つのスクリプトをどのボタンでも使い回せます。

using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoader : MonoBehaviour
{
    // On Click の欄にシーン名を打ち込んで使う(引数が string 1つの関数)
    public void LoadScene(string sceneName)
    {
        SceneManager.LoadScene(sceneName);
    }
}

On ClickでSceneLoader > LoadScene (string)を選ぶと、関数名の下に文字を入れる欄が出ます。そこに移動先のシーン名(例: GameScene)をそのまま入れてください。

移動先のシーンは、File > Build ProfilesのScene Listに入っている必要があります。入っていないと、押した瞬間にConsoleへ「Scene ‘〇〇’ couldn’t be loaded because it has not been added to…」で始まるエラーが出て、シーンは切り替わりません。Unity 2022までは同じ設定がFile > Build Settingsにありました。

シーン名のつづりが1文字でも違うと同じエラーになります。大文字と小文字も区別されるので、プロジェクトウィンドウのシーンのファイル名をコピーして貼ると確実です。リトライボタンの具体的な作り方は、的当てゲームのシリーズでも書いています。

ボタンの見た目を変える(色・画像・アニメーション)

マウスを乗せた時や押した時にボタンの色が少し変わるのは、ButtonのTransitionという設定のおかげです。初期値はColor Tintで、4種類から選べます。

Transition 変わり方 向いている場面
None 何も変わらない 自分で演出をつける時
Color Tint(初期値) 画像に色を掛けて明暗を変える まず動かしたい時
Sprite Swap 状態ごとに別の画像に差し替える 押したらへこむ絵など、素材がそろっている時
Animation 状態ごとにアニメーションを再生する 拡大・揺れ・光るなどの動き

Color Tintで色を調整する

Color Tintの初期値は、Normal Colorが白(255)、Highlighted Colorが245、Pressed Colorが200、Disabled Colorが200で半透明(アルファ128)です。つまり初期状態だと、押してもほんの少し暗くなるだけ。押した感じが弱いと思ったら、Pressed Colorをもっと暗くするか、色味を付けてみてください。Fade Durationの0.1は色が切り替わるまでの秒数です。

Color Tintは画像に色を「掛ける」仕組みなので、元の画像が黒っぽいと変化がほとんど見えません。暗いデザインのボタンなら、Sprite Swapの方が思った通りの見た目にしやすいですね。

Sprite Swapで画像を差し替える

ボタンの画像を自分のものにする時は、画像ファイルを選んでインスペクターのTexture TypeをSprite (2D and UI)にしてApplyしておきます。そのうえでButtonのImageのSource Imageに入れ、TransitionをSprite Swapに変えてHighlighted Sprite・Pressed Sprite・Disabled Spriteを入れれば完成です。

角丸のボタンを大きさを変えて使いたいなら、Sprite EditorでBorder(伸ばさない端の幅)を決め、ImageのImage TypeをSlicedにすると、角がつぶれずに伸びます。この手順はClassic RPG GUIの記事で、実際の素材を使って書きました。

Animationで動きをつける

TransitionをAnimationにすると、インスペクターに「Auto Generate Animation」というボタンが出ます。押して保存先を決めると、Normal・Highlighted・Pressed・Selected・Disabledの5つの状態を持ったAnimator Controllerが作られます。あとはAnimationウィンドウで、たとえばPressedのクリップにScaleを0.95にするキーを打てば「押すと縮む」ボタンになります。

動きにこだわりたい人は、このやり方より、DOTweenなどのアセットでスクリプトから動かす方が細かく作れます。ただ、まずは標準機能で形にしてみるのがおすすめです。

押せなくする・文字を変える(interactableと連打対策)

「送信」や「購入」のボタンは、2回押されると困ることがあります。押した直後にinteractableをfalseにすれば、そのボタンは反応しなくなり、見た目もDisabled Colorの色に変わります。ついでに文字も変えておくと、押した側にも状態が伝わりますね。

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class OneShotButton : MonoBehaviour
{
    [SerializeField] private Button button;          // 対象のボタン
    [SerializeField] private TMP_Text label;         // ボタンの子にある Text (TMP)

    private void Awake()
    {
        button.onClick.AddListener(OnPressed);
    }

    private void OnPressed()
    {
        // 押せない状態にする(Disabled Color の色になる)
        button.interactable = false;
        label.text = "送信中…";

        // ここで保存や通信などの処理を始める
        Debug.Log("一度だけ実行されます");
    }

    // 処理が終わったら呼んで、また押せるように戻す
    public void ResetButton()
    {
        button.interactable = true;
        label.text = "送信する";
    }
}

文字の型をTMP_Textにしているのは、ボタンの子に付いているのがTextMeshProUGUIだからです。古いTextを使ったLegacyのボタンではUnityEngine.UI.Textになります。ここを取り違えて、インスペクターにText (TMP)をドラッグできない、という相談はよく見かけます。

ボタンそのものを隠したい時はbutton.gameObject.SetActive(false)です。interactableは「見えているけど押せない」、SetActiveは「見えなくなる」と覚えておくと使い分けやすいと思います。

※ この記事のコードは、Unity 6000.3.14f1のUnityEngine.UI・TextMeshProのDLLを参照してビルドが通ることを確かめたものです。エディタ上の動作確認はしていないので、動かない時は下の確認表と合わせて見てください。

ボタンが押せない・反応しない時の確認

「ボタンを押しても何も起きない」は、uGUIでいちばん多いつまずきです。原因はだいたい決まっているので、上から順に見ていくのが早道。

症状 よくある原因 見る場所
色も変わらず完全に無反応 EventSystemがない ヒエラルキーにEventSystemがあるか
一部だけ押せない・端しか押せない 上に透明なImageやTextが重なっている 重なっているUIのRaycast Target
グレーのまま押せない interactableがオフ、または親のCanvas Groupで止めている ButtonのInteractable、親のCanvas Group
色は変わるのに処理が動かない On Clickに何も登録されていない、別のオブジェクトを入れている On Clickの欄、Consoleのエラー
関数が一覧に出てこない privateになっている、引数が2つ以上 関数の宣言
押すとエラーが出る 入力の設定とInput Moduleが合っていない EventSystemのInput Module

EventSystemがない・2つある

ボタンへのクリックを受け取って届けているのはEventSystemです。ボタンをコピーして別のシーンに貼った時など、EventSystemがないシーンではボタンが一切反応しません。GameObject > UI (Canvas) > Event Systemで足せます。

逆に、シーンを追加読み込みしてEventSystemが2つになると、Consoleに「There are 2 event systems in the scene」という警告が出ます。どちらか1つにしてください。

透明なUIが上に重なっている

uGUIは、ヒエラルキーで下にあるものほど手前に描かれます。ボタンの後に、画面全体を覆うPanelや、透明にしたImageを置いていると、クリックはそちらに吸われてボタンまで届きません。見た目では透明でも、Raycast Targetにチェックが入っていればクリックを受け止めてしまうからです。

対処は、手前のUIのImageやText (TMP)でRaycast Targetのチェックを外すか、ヒエラルキーでボタンを下へ移すこと。どれが邪魔しているか分からない時は、再生中にEventSystemを選んでみてください。インスペクターの一番下のプレビュー欄に入力の状態が出ていて、「pointerEnter」の行がいまマウスの下にあるオブジェクトです。

ボタンの子のText (TMP)が大きすぎてボタンの外まではみ出していても、文字の部分が反応してしまいます。押せる範囲が思ったよりずれている時はここも疑ってみてください。

Canvas Groupとinteractable

親のオブジェクトにCanvas Groupが付いていて、InteractableやBlocks Raycastsがオフになっていると、その下のボタンはすべて押せません。メニューをフェードで消すためにCanvas Groupを使った後、戻し忘れることがよくあります。

Input SystemだけにしているとStandaloneInputModuleでエラーになる

Unity 6の新規プロジェクトは、入力が新しいInput Systemになっていることが多いです。Input Systemのパッケージが入っていれば、メニューから作ったEventSystemには最初からInput System UI Input Moduleが付きます。ところが、古いプロジェクトやアセットから持ってきたEventSystemには古いStandalone Input Moduleが付いたまま。これを使うと、再生した時に「You are trying to read Input using the UnityEngine.Input class」というエラーが出て、ボタンが反応しません。

EventSystemを選ぶと、インスペクターのStandalone Input Moduleに「Replace with InputSystemUIInputModule」というボタンが出るので、押して置き換えてください。Edit > Project Settings > PlayerのActive Input HandlingをBothにする手もありますが、新しく作るゲームなら置き換える方が素直です。

Consoleに出る赤いエラー全般の読み方は、こちらにまとめています。

デザインまで手早く済ませたいならUIアセットも

ここまでの手順で、押せるボタンは標準機能だけで作れます。ただ、ゲームらしい見た目のボタンを状態ごとに何枚も描くのは、なかなか骨の折れる作業です。

素材をそろえる時間を短くしたいなら、Asset StoreのUIアセットを使う手もあります。無料のUI Examplesやボタンの演出を付けられるLean GUIから、ボタン・ウィンドウ・アイコンがまとまった有料のパックまで、タイプ別に比べた記事を書いたので、選ぶ時の参考にしてください。

RPG向けの枠やボタンの素材を、Sprite Swap・Slicedで使う例はClassic RPG GUIの紹介に載せています。UI以外も含めて、よく使われているアセットを探すなら40選がまとまっています。

有料のUIパックは、Asset Storeのセールで半額前後になることがあります。急ぎでなければ、セールの時期を待ってから買うのも1つの手ですね。

まとめ

Unityのボタンは、UI (Canvas) > Button – TextMeshProで置き、publicな関数をOn Clickに登録すれば動きます。スクリプトでまとめて登録するならAddListener、シーン移動はSceneManager.LoadSceneとBuild ProfilesのScene List。見た目はTransitionの4種類から選び、2回押されたくないボタンはinteractableで止めます。

押しても反応しない時は、EventSystem、手前に重なった透明なUI、Canvas Group、Input Moduleの順に見ていけば、たいてい原因が見つかるはずです。

スコアやHPを画面に出す方法はuGUIでステータスを表示する記事で書いています。UI素材を安く手に入れたい人は、毎月更新しているAsset Storeのセール情報も合わせてのぞいてみてください。

著者プロフィール
うんくん

IT系に特化した記事がメインです。(Unity多め。)
Udemy講師もやっています。よろしくお願いします。

※Amazonのアソシエイトとして、当メディアは適格販売により収入を得ています。

うんくんをフォローする
unityunity基本操作
スポンサーリンク
シェアする
うんくんをフォローする