Canvasにボタンを置いた。見た目もそれっぽい。再生して押してみると——何も起きない。コンソールにエラーすら出ないので、どこが悪いのか見当もつかない。UIを触り始めた人が最初にハマる、定番の沼です。
この記事では、Buttonコンポーネントの使い方(onClickの2つの登録方法)から、クリックがボタンに届くまでの仕組み、反応しないとき の3大原因、そして「押した感」の作り方までを一気に整理します。Canvasやアンカーの土台は Canvas入門 を先にどうぞ。
この記事でわかること
- onClickの2つの登録方法——Inspector登録とAddListener
- クリックが届くまでの仕組み——EventSystem・Graphic Raycaster・Raycast Target
- ボタンが反応しない3大原因とその直し方
- Transitionで「押した感」を作る
- IPointerDown/Upで長押しボタンを作る
- 実践:ポーズメニューを組む
Buttonの最小セットアップ
Hierarchyで右クリック →「UI > Button - TextMeshPro」を選ぶと、必要なものが一式そろいます。
- Canvas: UIの土台(なければ自動生成)
- Button:
Image(見た目)+Button(クリック処理)を持つ本体。子にTextMeshProのラベル - EventSystem: 入力をUIへ届ける司令塔(なければ自動生成)
この時点で再生してボタンにカーソルを載せると、色がわずかに変わるはずです。変わるなら、クリックを受け取る仕組みはもう動いています。あとは「押されたら何をするか」を教えるだけです。
onClickに処理を登録する2つの方法
Buttonの心臓部が onClick——「クリックされた瞬間に呼ぶ処理のリスト」です。登録方法は2つあります。
方法1: Inspectorで登録する(画面に置いてあるUI向き)
- 呼びたいメソッドを
publicで用意します。
using UnityEngine;
public class TitleMenu : MonoBehaviour
{
public void StartGame()
{
Debug.Log("ゲーム開始!");
// SceneManager.LoadScene("Main"); など
}
}
- 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」と覚えてください。
クリックがボタンに届くまでの仕組み
トラブルを直せるようになるために、クリックがonClickに届くまでの経路を知っておきましょう。登場人物は3人です。

- EventSystem(シーンの司令塔): マウス・タッチなどの入力を毎フレーム監視し、「今どこがクリックされたか」を管理します。シーンに1つ必要です。
- Graphic Raycaster(Canvasの検問所): Canvasに付いていて、クリック位置に光線(Raycast)を飛ばし、当たったUIを探します。
- Raycast Target(各UIの「当たり判定スイッチ」):
ImageやTextMeshProが持つチェックボックスです。オンのUIだけが光線に当たります。当たったUIのうち一番手前の1つだけがクリックを受け取ります。
この経路のどこかが切れると、ボタンは沈黙します。つまり——
ボタンが反応しない3大原因
エラーが出ないぶん厄介な「無反応」は、ほぼこの3つです。上から順に確認してください。

- シーンにEventSystemがない: 整理中に誤って消してしまうのが定番です。Hierarchyを検索して、なければ「UI > Event System」で作り直します。全UIが一斉に無反応なら、ほぼこれです。
- Raycast Targetがオフ: ボタンの
ImageのRaycast Targetが外れていると、光線が素通りします。逆に、クリックさせないUI(飾りのアイコンやラベル)はオフにするのが正解なので、オン・オフの意図を持って管理します。 - 透明なUIが手前を覆っている: 全画面サイズの透明パネルや、大きすぎるテキストの矩形が、ボタンの手前でクリックを横取りしているケースです。見た目では分からないのが罠です。エディタ再生中に「Window > Analysis > Event SystemのInspector」を見ると、カーソル下で光線に当たっているUIが表示されるので、犯人を特定できます。
tips 3の犯人はたいてい「飾りのつもりで置いた大きな透明Image」です。飾りUIのRaycast Targetを切る習慣があれば、このトラブルは激減します。
Transitionで「押した感」を作る
機能したら、次は手応えです。ButtonのTransitionは、状態(通常・カーソル載せ・押下・無効)ごとに見た目を切り替える仕組みです。

| モード | 仕組み | 向いている場面 |
|---|---|---|
| Color Tint(既定) | Imageの色を掛け算で変える | まずはこれで十分 |
| Sprite Swap | 状態ごとに画像を差し替える | 「押し込まれた絵」を用意できるとき |
| Animation | Animatorでアニメさせる | 凝った演出(弾む・光るなど) |
既定の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(外れた)があり、ツールチップ表示などに使えます。
実践:ポーズメニューを組む
RPGのメニュー画面、アクションの一時停止、パズルの「やりなおし」確認——「開く→選ぶ→閉じる」のメニューは、この記事の部品だけで組める、ボタンUIの登竜門です。ポーズメニューを通しで作ります。

UIの組み立て:
- Canvasの下に
PausePanel(全画面のImage・黒の半透明)を作ります。後ろのゲーム画面を暗く沈ませ、同時に背後のUIへのクリックを遮断する役も果たします(Raycast Targetオン=3大原因の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を付けてinteractableとblocksRaycastsを切り替えます。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をオフにする習慣を。
ボタンはプレイヤーがゲームに触れる最初の接点です。あなたのゲームのボタン、押した瞬間に「ちゃんと応えて」いますか?