【Unity】Unity UIボタン入門:onClick・EventSystem・押し心地の作り方

作成: 2026-07-10

ボタンを置いたのに押しても反応しない——初心者が最初にハマるUIトラブルです。onClickの2つの登録方法、クリックが届くまでの仕組み(EventSystem・Graphic Raycaster)、反応しない3大原因、Transitionによる押し心地、長押しの実装までを解説します。

Canvasにボタンを置いた。見た目もそれっぽい。再生して押してみると——何も起きない。コンソールにエラーすら出ないので、どこが悪いのか見当もつかない。UIを触り始めた人が最初にハマる、定番の沼です。

この記事では、Buttonコンポーネントの使い方(onClickの2つの登録方法)から、クリックがボタンに届くまでの仕組み、反応しないときの3大原因、そして「押した感」の作り方までを一気に整理します。Canvasやアンカーの土台は Canvas入門 を先にどうぞ。

UIボタンのイメージ。大きなボタンが指で押し込まれ、クリックの波紋が広がっている

この記事でわかること

  • onClickの2つの登録方法——Inspector登録とAddListener
  • クリックが届くまでの仕組み——EventSystem・Graphic Raycaster・Raycast Target
  • ボタンが反応しない3大原因とその直し方
  • Transitionで「押した感」を作る
  • IPointerDown/Upで長押しボタンを作る
  • 実践:ポーズメニューを組む

Sponsored

Buttonの最小セットアップ

Hierarchyで右クリック →「UI > Button - TextMeshPro」を選ぶと、必要なものが一式そろいます。

  • Canvas: UIの土台(なければ自動生成)
  • Button: Image(見た目)+Button(クリック処理)を持つ本体。子にTextMeshProのラベル
  • EventSystem: 入力をUIへ届ける司令塔(なければ自動生成)

この時点で再生してボタンにカーソルを載せると、色がわずかに変わるはずです。変わるなら、クリックを受け取る仕組みはもう動いています。あとは「押されたら何をするか」を教えるだけです。

onClickに処理を登録する2つの方法

Buttonの心臓部が onClick——「クリックされた瞬間に呼ぶ処理のリスト」です。登録方法は2つあります。

方法1: Inspectorで登録する(画面に置いてあるUI向き)

  1. 呼びたいメソッドをpublicで用意します。
using UnityEngine;

public class TitleMenu : MonoBehaviour
{
    public void StartGame()
    {
        Debug.Log("ゲーム開始!");
        // SceneManager.LoadScene("Main"); など
    }
}
  1. ButtonのInspectorの On Click() リストで「+」を押し、TitleMenuが付いたオブジェクトをドラッグして、ドロップダウンからTitleMenu.StartGameを選びます。

コードとの結線がInspectorで見えるのが利点です。タイトル画面のように、シーンに最初から置いてあるボタンはこの方法が手軽です。

方法2: スクリプトでAddListenerする(動的なUI向き)

using UnityEngine;
using UnityEngine.UI;

public class ShopItemSlot : MonoBehaviour
{
    [SerializeField] private Button buyButton;

    void Start()
    {
        // クリック時に呼ぶ処理をコードから登録する
        buyButton.onClick.AddListener(OnBuyClicked);
    }

    private void OnBuyClicked()
    {
        Debug.Log("購入!");
    }

    void OnDestroy()
    {
        // 登録したら外すのが行儀の良い作法
        buyButton.onClick.RemoveListener(OnBuyClicked);
    }
}

ショップの商品リストのように実行中に生成されるボタンは、Inspectorで事前に結線できないためAddListener一択です。「置いてあるUIはInspector、生まれるUIはAddListener」と覚えてください。

Sponsored

クリックがボタンに届くまでの仕組み

トラブルを直せるようになるために、クリックがonClickに届くまでの経路を知っておきましょう。登場人物は3人です。

クリック検知の流れ。クリック入力をEventSystemが受け取り、CanvasのGraphic RaycasterがRaycast TargetがオンのUIを探して、一番手前のUIに届ける。それがButtonならonClickが発火する
  1. EventSystem(シーンの司令塔): マウス・タッチなどの入力を毎フレーム監視し、「今どこがクリックされたか」を管理します。シーンに1つ必要です。
  2. Graphic Raycaster(Canvasの検問所): Canvasに付いていて、クリック位置に光線(Raycast)を飛ばし、当たったUIを探します
  3. Raycast Target(各UIの「当たり判定スイッチ」): ImageTextMeshProが持つチェックボックスです。オンのUIだけが光線に当たります。当たったUIのうち一番手前の1つだけがクリックを受け取ります。

この経路のどこかが切れると、ボタンは沈黙します。つまり——

ボタンが反応しない3大原因

エラーが出ないぶん厄介な「無反応」は、ほぼこの3つです。上から順に確認してください。

ボタンが反応しない3大原因。1=シーンにEventSystemがない、2=ボタンのRaycast Targetがオフ、3=透明なUIがボタンの手前を覆っている
  1. シーンにEventSystemがない: 整理中に誤って消してしまうのが定番です。Hierarchyを検索して、なければ「UI > Event System」で作り直します。全UIが一斉に無反応なら、ほぼこれです。
  2. Raycast Targetがオフ: ボタンのImageRaycast Targetが外れていると、光線が素通りします。逆に、クリックさせないUI(飾りのアイコンやラベル)はオフにするのが正解なので、オン・オフの意図を持って管理します。
  3. 透明なUIが手前を覆っている: 全画面サイズの透明パネルや、大きすぎるテキストの矩形が、ボタンの手前でクリックを横取りしているケースです。見た目では分からないのが罠です。エディタ再生中に「Window > Analysis > Event SystemのInspector」を見ると、カーソル下で光線に当たっているUIが表示されるので、犯人を特定できます。

tips 3の犯人はたいてい「飾りのつもりで置いた大きな透明Image」です。飾りUIのRaycast Targetを切る習慣があれば、このトラブルは激減します。

Transitionで「押した感」を作る

機能したら、次は手応えです。ButtonTransitionは、状態(通常・カーソル載せ・押下・無効)ごとに見た目を切り替える仕組みです。

Transitionの状態変化。Normal=通常、Highlighted=カーソルが載って少し明るく、Pressed=押されて暗く沈み、Disabled=無効でグレーアウト
モード仕組み向いている場面
Color Tint(既定)Imageの色を掛け算で変えるまずはこれで十分
Sprite Swap状態ごとに画像を差し替える「押し込まれた絵」を用意できるとき
AnimationAnimatorでアニメさせる凝った演出(弾む・光るなど)

既定のColor Tintなら、Pressed Colorを暗め(例: RGB 200,200,200) にするだけで「押した感」が出ます。ゲームらしい手応えを足すなら、押下時のSEをonClickと同じ場所で鳴らすのが定石です(オーディオ入門)。

長押しボタンを作る——IPointerDown/Up

onClickが発火するのは「押して離した瞬間」です。「長押しでアイテムを削除」「ホールドでチャージ」のような操作は、押した瞬間離した瞬間を別々に取る必要があります。そこで使うのがIPointerDownHandler/IPointerUpHandlerインターフェースです。

長押しボタンの図。ボタンを押し続けるとゲージが円形に満ちていき、満タンで発動する。途中で離すとゲージはリセットされる
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;

public class HoldButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    [SerializeField] private float holdTime = 1.0f;   // 発動までの秒数
    [SerializeField] private Image gaugeImage;        // Fill AmountでゲージにするImage
    public UnityEvent onHoldComplete;                 // 満タンになったら呼ぶ処理

    private float timer;
    private bool holding;

    public void OnPointerDown(PointerEventData eventData) => holding = true;

    public void OnPointerUp(PointerEventData eventData)
    {
        holding = false;
        timer = 0f; // 途中で離したらリセット
    }

    void Update()
    {
        if (!holding) return;

        timer += Time.deltaTime;
        gaugeImage.fillAmount = timer / holdTime; // ゲージを満たしていく

        if (timer >= holdTime)
        {
            holding = false;
            timer = 0f;
            onHoldComplete.Invoke(); // 発動!
        }
    }

    void LateUpdate()
    {
        if (!holding && gaugeImage.fillAmount > 0f)
        {
            gaugeImage.fillAmount = 0f;
        }
    }
}

このスクリプトをボタン(またはImage)に付けるだけで動きます。Image Type: Filledにしたゲージ用Imageを重ねれば、押している間ゲージが満ちていく定番UIの完成です。ほかにもIPointerEnterHandler(カーソルが載った)・IPointerExitHandler(外れた)があり、ツールチップ表示などに使えます。

Sponsored

実践:ポーズメニューを組む

RPGのメニュー画面、アクションの一時停止、パズルの「やりなおし」確認——「開く→選ぶ→閉じる」のメニューは、この記事の部品だけで組める、ボタンUIの登竜門です。ポーズメニューを通しで作ります。

ポーズメニューの完成図。ゲーム画面の上に半透明の暗いパネルが重なり、中央に「つづける」「リトライ」「タイトルへ」の3つのボタンが縦に並んでいる

UIの組み立て:

  1. Canvasの下にPausePanel(全画面のImage・黒の半透明)を作ります。後ろのゲーム画面を暗く沈ませ、同時に背後のUIへのクリックを遮断する役も果たします(Raycast Targetオン=3大原因の3を、今度は意図的に使うわけです)
  2. その子に、ボタンを3つ縦に並べます——「つづける」「リトライ」「タイトルへ」
  3. PausePanelは最初は非アクティブにしておきます

スクリプト——開閉と3つのボタンの行き先です。

using UnityEngine;
using UnityEngine.SceneManagement;

public class PauseMenu : MonoBehaviour
{
    [SerializeField] private GameObject pausePanel;

    private bool isPaused;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (isPaused) Resume();
            else Pause();
        }
    }

    public void Pause()
    {
        isPaused = true;
        pausePanel.SetActive(true);
        Time.timeScale = 0f; // ゲーム内の時間を止める
    }

    // 「つづける」ボタンのOn Click()に登録
    public void Resume()
    {
        isPaused = false;
        pausePanel.SetActive(false);
        Time.timeScale = 1f;
    }

    // 「リトライ」ボタンに登録
    public void Retry()
    {
        Time.timeScale = 1f; // 戻し忘れると次のシーンも止まったまま!
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    // 「タイトルへ」ボタンに登録
    public void GoToTitle()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene("Title");
    }
}

各ボタンのOn Click()に対応するメソッドをInspectorで登録すれば完成です。Time.timeScale = 0でゲームは止まりますが、UIのクリックは通常どおり動く——これがポーズメニューが成立する理由です。

ポイントは2つです。 「半透明パネルにクリック遮断の仕事もさせる」(ポーズ中に背後のゲームUIが誤反応する事故を、パネル1枚が防いでくれます)と、 「シーンを離れる前にtimeScaleを必ず戻す」(戻し忘れると、リトライ後の世界が止まったままになります。ポーズの奥深い注意点——音・コルーチン・Unscaled時間——は ポーズ実装ガイド が専門記事です)。

おまけ:先に知っておくと良いこと

  • 連打・二度押しの防止: 「購入」のような取り返しのつかないボタンは、押された直後にbutton.interactable = false;で無効化するのが定石です。Disabled Colorでグレーアウトも自動で掛かります。
  • まとめて無効化するならCanvasGroup: パネル内のボタンを一括で押せなくしたいときは、親にCanvasGroupを付けてinteractableblocksRaycastsを切り替えます。1個ずつ触るより堅牢です。
  • EventTriggerコンポーネントは最終手段: InspectorだけでPointerDown等を扱えるEventTriggerもありますが、付けたUIがすべてのイベントを吸ってしまう副作用があります。コードでインターフェースを実装する方が安全です。
  • ゲームパッド対応: ボタンをパッドで選択・決定するには、EventSystemのInput System UI Input Moduleと、ButtonのNavigation設定が関わってきます。入力側の土台は Input System入門 を参照してください。

まとめ

  • onClickの登録は2通り—— 置いてあるUIはInspector、動的に生まれるUIはAddListener
  • クリックは EventSystem → Graphic Raycaster → Raycast TargetがオンのUI の経路で届く。
  • 無反応の3大原因は EventSystem消失・Raycast Targetオフ・透明UIの横取り。この順に疑う。
  • 押した感はTransition(まずはPressed Colorを暗く)+押下SE。
  • 長押しはonClickでは取れない——IPointerDown/Upで自作する。
  • 飾りのUIはRaycast Targetをオフにする習慣を。

ボタンはプレイヤーがゲームに触れる最初の接点です。あなたのゲームのボタン、押した瞬間に「ちゃんと応えて」いますか?