Unity
Unity2D
uGUI

【uGUI】Buttonの使い方

More than 3 years have passed since last update.

はじめに

uGUIで最も使用頻度が高いと思われるButtonの基本的な使い方を紹介します

Buttonの作り方

まずはButtonの作り方からです。
メインメニューから GameObject > UI > Button を選択します。

001.png

Buttonを選ぶと、Hierarchyビューに、Canvas / Button / Text / EventSystem の4つのGameObjectが追加されます。

002.png

以下、簡単な説明です。

  • Canvas: UIを乗せる領域。これがないとUIは作れない
  • Button: ボタン。ここにボタンの挙動を設定する
  • Text: ボタンの上に乗るテキスト
  • EventSystem: UIイベントスクリプト管理。イベントを拡張したい場合はここに手を入れる

今回は、Buttonのクリック判定ができればいいので、EventSystemはそのまま使います。

ボタンの見た目の挙動を制御する

Hierarchyビューから Button を選択します。するとInspectorビューにボタンの情報が表示されます。

003.png

パラメータが多くて混乱しそうですが、まずは Button (Script) に注目します。ここでボタンの見た目を変更できます。

ここにある4つのColorパラメータに注目します。

  • Normal Color: 初期の色
  • Highlighted Color: マウスが上に乗った時の色
  • Pressed Color: ボタンを押した時の色
  • Disabled Color: ボタン無効化時の色

このパラメータを以下のように変更します。

004.png

  • Normal Color: 白(そのまま)
  • Highlighted Color: 赤
  • Pressed Color: 青
  • Disabled Color: 灰色(そのまま)

実行してみると、ボタンの上にマウスが乗ると「赤」になり、クリックすると「青」、その後は「赤」になりました。

  1. 初期状態→白
  2. ボタンの上にマウスが乗る→赤
  3. クリックする→青
  4. クリックを解除→赤

クリック解除時に赤になるのは、そのボタンにフォーカスがあるからです。画面内のどこかをクリックすると、フォーカスが外れて、白に戻ります。これは ナビゲーションモード と呼ばれ、キーボードでの操作に対応するためのものとなります。この機能が不要な場合は NavigationNone に変更すること無効化できます。

005.png

丸ボタンにする

このような丸いボタンを作ってみます。
029.png

Buttonの Image (Script) から Source Image を選択して、Knobスプライトを選択します。
006.png

すると四角いボタンが楕円形になります。

007.png

これを丸く変形させます。

Hierarchyビューの Buttonオブジェクトをダブルクリックしてズームし、Sceneビューの上にあるツールバーから四角形のアイコンを選びます。

008.png

このアイコンを選ぶと、オブジェクトの変形モードとなり、端にある青い丸をドラッグするとオブジェクトを変形できます。

009.png

変形させて丸くしてみました。

010.png

クリックしたときの処理を実装する

ボタンをクリックしたときの処理を実装します。
メインメニューから Assets > Create > C# Script を選択して、スクリプトを追加します。

011.png

スクリプトの名前はMyButtonとしておきます。
012.png

そうしたらスクリプトを開いて以下のように記述します。

MyButton.cs
using UnityEngine;
using System.Collections;

public class MyButton : MonoBehaviour {

  /// ボタンをクリックした時の処理
  public void OnClick() {
    Debug.Log("Button click!");
  }
}

ボタンをクリックすると、"Button click!"というログを出力するようにしました。
スクリプトが書けたら保存して、Unityエディタに戻り、MyButtonスクリプトを、Buttonオブジェクトにドラッグ&ドロップします。

013.png

次にHierarchyビューからButtonオブジェクトを選択して、Inspectorビューの Button (Script) コンポーネントから OnClick() の枠にある+ボタンをクリックします。

014.png

すると項目が追加されるので、○をクリックしてオブジェクト選択ダイアログを表示します。

015.png

そこからButtonオブジェクトを選択します。
次にとなりにある項目の No Functionをクリックすると、どの関数を呼び出すのかを選択するリストが表示されるので、MyButton > OnClick ()を選びます。

016.png

実行してみます。ボタンをクリックすると、"Button click!"というログが出力されるはずです。

017.png

クリック処理を行うまでの流れの整理

ここまでの流れを整理すると以下のようになります。

018.png

OnClickには、「誰(主体者)」が「何をする(動作)」の設定をします。今回であればButtonオブジェクトが主体者で、それが持つMyButtonスクリプトのOnClick()という動作を呼び出す、という設定をしていることとなります。

Buttonの表示・非表示を切り替える

Buttonの表示・非表示を切り替えるには GameObject.SetActive()を使用します。例えば、MyButtonスクリプトを以下のように修正します。

MyButton.cs
using UnityEngine;
using System.Collections;

public class MyButton : MonoBehaviour {

  public void OnClick() {
    Debug.Log("Button click!");
    // 非表示にする
    gameObject.SetActive(false);
  }
}

実行してボタンをクリックすると、ボタンが非表示になります。ただ、この方法には問題があります。SetActive(false) をすると、そのオブジェクトは GameObject.Find で取得しようとしても検索対象外となってしまい、非表示したボタンを再表示することができなくなります。
そこで親のCanvasオブジェクトから、子の階層をたどってButtonオブジェクトを取得する方法を使います。
メインメニューから Assets > Create > C# Script でスクリプトを追加し、スクリプトの名前は MyCanvas とします。
スクリプトは以下のように記述します。

MyCanvas.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI; // ←※ここを追加する

public class MyCanvas : MonoBehaviour {

  static Canvas _canvas;
  void Start () {
    // Canvasコンポーネントを保持
    _canvas = GetComponent<Canvas>();
  }

  /// 表示・非表示を設定する
  public static void SetActive(string name, bool b) {
    foreach(Transform child in _canvas.transform) {
      // 子の要素をたどる
      if(child.name == name) {
        // 指定した名前と一致
        // 表示フラグを設定
        child.gameObject.SetActive(b);
        // おしまい
        return;
      }
    }
    // 指定したオブジェクト名が見つからなかった
    Debug.LogWarning("Not found objname:"+name);
  }
}

このスクリプトは生成時(Start)にCanvasコンポーネントを保持します。そして、SetActive関数を呼び出すと、Canvasの子要素から一致するオブジェクト名を探し表示・非表示の設定を行います。
そしてこのスクリプトを、Canvasオブジェクトにドラッグ&ドロップしてアタッチします。
これでボタンの表示・非表示を、外部から切り替えられるようになります。
確認用に、もう1つボタンを配置して、交互に表示を切り替えるようにしています。

  1. メインメニューからGameObject > UI > Button でボタンを作成します
  2. 作成したボタンの名前はButton2に変更します
    020.png

  3. メインメニューからAssets > Create > C# Scriptでスクリプトを作成します

  4. スクリプトの名前はMyButton2に変更します
    021.png

  5. MyButton2スクリプトButton2オブジェクトにドラッグ&ドロップしてアタッチします
    022.png

MyButton2スクリプトを開いて以下のように編集します。

MyButton2
using UnityEngine;
using System.Collections;

public class MyButton2 : MonoBehaviour {

  public void OnClick() {
    Debug.Log("Button2 click!");
    // 非表示にする
    gameObject.SetActive(false);
    // Buttonを表示する
    MyCanvas.SetActive("Button", true);
  }
}

これで自分自身を非表示にした後、別のButtonを表示できるようになります。
ついでにMyButtonスクリプトも修正しておきます。

MyButton.cs
using UnityEngine;
using System.Collections;

public class MyButton : MonoBehaviour {

  public void OnClick() {
    Debug.Log("Button click!");
    // 非表示にする
    gameObject.SetActive(false);
    // Button2を表示する(※ここを追加)
    MyCanvas.SetActive("Button2", true);
  }
}

Button2のOnClickを設定します。HierarchyビューからButton2オブジェクトを選び、Inspectorビューから、Button (Script)コンポーネントの設定をします。

023.png

手順は、

  1. On Click()+ボタンをクリック
  2. Button2オブジェクトを設定
  3. MyButton2 > OnClickを登録

となります。

実行して動作を確認します。クリックしたボタンが消え、別のボタンをクリックすると別のボタンが再表示されるようになります。

027.png

ボタンのテキストを変更する

ボタンの子にTextオブジェクトがある、という前提であれば、 ボタンオブジェクトの子の要素を調べる ことで、Textオブジェクトを取得することができます。

028.png

このような階層になっていることが前提とします。この場合、前に説明した MyCanvasスクリプトに以下の関数を追加します。

MyCanvas.cs
  /// テキストを変更する
  public static void SetText(string name, string text) {
    foreach(Transform child in _canvas.transform) {
      // 子の要素をたどる
      if(child.name == name) {
        // 名前が一致
        foreach(Transform child2 in child.transform) {
          // 孫要素をたどる
          if(child2.name == "Text") {
            // テキストを見つけた
            Text t = child2.GetComponent<Text>();
            // テキスト変更
            t.text = text;

            // おしまい
            return;
          }
        }
      }
    }
    // 指定したオブジェクト名が見つからなかった
    Debug.LogWarning("Not found objname:"+name);
  }

該当するButtonオブジェクトを探し、さらにその子要素Textを探し、テキスト文字列を設定します。

使い方は、例えばこんな感じです。

MyButton.cs
    MyCanvas.SetText("Button2", "New Button");

注意点

「テキストを変更」するスクリプトはGetComponentを使用しているため若干重たい処理となります。なので、頻繁に更新する場合は、一番最初にTextオブジェクトを直接取得し、それ以降はTextオブジェクトに直接設定する方が処理が軽くなります。

MyCanvas.cs
  /// 指定のオブジェクトのTextを探す
  public static Text FindText(string name) {
    foreach(Transform child in _canvas.transform) {
      // 子の要素をたどる
      if(child.name == name) {
        // 指定した名前と一致
        foreach(Transform child2 in child.transform) {
          // 孫要素をたどる
          if(child2.name == "Text") {
            return child2.GetComponent<Text>();
          }
        }
      }
    }
    // 指定したオブジェクト名が見つからなかった
    Debug.LogWarning("Not found objname:"+name);
    return null;
  }

使う側のサンプルです。

GameMgr.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class GameMgr : MonoBehaviour {
  Text text;
  void Start() {
    // 開始時に取得
    text = MyCanvas.FindText("Button2");
  }

  void Update() {
    // 毎フレームテキストを設定
    text.text = "" + Time.deltaTime;
  }

ボタンを無効化する

ボタンを無効化するには、Buttonコンポーネントinteractableフラグを使用します。
例えば、MyCanvasクラスに以下の関数を実装します。

MyCanvas.cs
  /// ボタンの有効・無効を設定する
  public static void SetInteractive(string name, bool b) {
    foreach(Transform child in _canvas.transform) {
      // 子の要素をたどる
      if(child.name == name) {
        // 指定した名前と一致
        // Buttonコンポーネントを取得する
        Button btn = child.GetComponent<Button>();
        // 有効・無効フラグを設定
        btn.interactable = b;
        // おしまい
        return;
      }
    }
    // 指定したオブジェクト名が見つからなかった
    Debug.LogWarning("Not found objname:"+name);
  }

例えばこんな関数を作っておけば、他のクラスから有効・無効が設定できます。

MyButton.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class MyButton : MonoBehaviour {

  public void OnClick() {
    Debug.Log("Button click!");
    // 自分自身を無効化する
    MyCanvas.SetInteractive("Button", false);
  }
}

こんな感じでグレーになって無効化しました(左の丸ボタン)。

000.png

この状態になると、クリックしても何も反応しなくなります。