【uGUI】Buttonの使い方


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

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

![001.png](https://qiita-image-store.s3.amazonaws.com/0/43772/543d5e8f-0039-eacb-70e2-96ccddabd621.png "001.png")

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

![002.png](https://qiita-image-store.s3.amazonaws.com/0/43772/b264f440-3ecd-d05e-7c98-cdce69378716.png "002.png")

以下、簡単な説明です。

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

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

## ボタンの見た目の挙動を制御する
Hierarchyビューから `Button` を選択します。するとInspectorビューにボタンの情報が表示されます。

![003.png](https://qiita-image-store.s3.amazonaws.com/0/43772/303d3385-0fc7-0518-fc3a-85057bc2ae6f.png "003.png")

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

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

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

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

![004.png](https://qiita-image-store.s3.amazonaws.com/0/43772/b79eed90-b150-ba9b-c276-d0c7a04979a2.png "004.png")

* Normal Color: 白（そのまま）
* Highlighted Color: 赤
* Pressed Color: 青
* Disabled Color: 灰色（そのまま）

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

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

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

![005.png](https://qiita-image-store.s3.amazonaws.com/0/43772/49aaa0b9-9531-3cbd-26f3-e83e1a4af684.png "005.png")

## 丸ボタンにする
このような丸いボタンを作ってみます。
![029.png](https://qiita-image-store.s3.amazonaws.com/0/43772/edf95842-c836-9d14-9f45-d71d37f37916.png "029.png")

Buttonの `Image (Script)` から `Source Image` を選択して、`Knob`スプライトを選択します。
![006.png](https://qiita-image-store.s3.amazonaws.com/0/43772/e512383f-b220-42bc-254e-624e3790bf34.png "006.png")

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

![007.png](https://qiita-image-store.s3.amazonaws.com/0/43772/7a0aed98-f800-89c1-3923-e43efa5c97ce.png "007.png")

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

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

![008.png](https://qiita-image-store.s3.amazonaws.com/0/43772/0bcd6e2b-ff32-cf7d-fdc5-ab6b25091a4d.png "008.png")

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

![009.png](https://qiita-image-store.s3.amazonaws.com/0/43772/3af0af9a-4954-cc13-031f-c09afca67971.png "009.png")

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

![010.png](https://qiita-image-store.s3.amazonaws.com/0/43772/f1edf6df-4daa-cdd9-4921-9711dbe38b46.png "010.png")

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

![011.png](https://qiita-image-store.s3.amazonaws.com/0/43772/d1f68541-ea8c-342d-a613-9f327cda1d7c.png "011.png")

スクリプトの名前は`MyButton`としておきます。
![012.png](https://qiita-image-store.s3.amazonaws.com/0/43772/b8654a99-4d69-ded3-0db3-e6eb46c526c9.png "012.png")

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

```csharp: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](https://qiita-image-store.s3.amazonaws.com/0/43772/730b0d6e-ef03-7d11-bbc0-b34efb87ea8a.png "013.png")

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

![014.png](https://qiita-image-store.s3.amazonaws.com/0/43772/fa385fb2-e876-6161-c94b-fb58dc8ebb71.png "014.png")

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

![015.png](https://qiita-image-store.s3.amazonaws.com/0/43772/534a832c-bf5a-b76a-5cff-4c4be6807f66.png "015.png")

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

![016.png](https://qiita-image-store.s3.amazonaws.com/0/43772/8ff0c05c-42f7-5d1b-4091-8d06854e100c.png "016.png")

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

![017.png](https://qiita-image-store.s3.amazonaws.com/0/43772/db1cb8b0-fc29-83eb-ed21-5562c6d35dd5.png "017.png")

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

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

![018.png](https://qiita-image-store.s3.amazonaws.com/0/43772/467e5d27-cd22-9f95-20fb-3a9262f0dcc2.png "018.png")

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

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

```csharp: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` とします。
スクリプトは以下のように記述します。

```csharp: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](https://qiita-image-store.s3.amazonaws.com/0/43772/f7b0c8b4-63ab-4d04-fc05-06d92e4f4555.png "020.png")

3. メインメニューから`Assets > Create > C# Script`でスクリプトを作成します
4. スクリプトの名前は`MyButton2`に変更します
![021.png](https://qiita-image-store.s3.amazonaws.com/0/43772/c011b350-8c8a-f252-f17d-e721ac8884a6.png "021.png")

5. `MyButton2スクリプト`を`Button2オブジェクト`にドラッグ＆ドロップしてアタッチします
![022.png](https://qiita-image-store.s3.amazonaws.com/0/43772/60ea1048-98d0-f2c8-780a-c574d5a36669.png "022.png")

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

```csharp: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`スクリプトも修正しておきます。

```csharp: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](https://qiita-image-store.s3.amazonaws.com/0/43772/f1b79252-db8e-3705-d503-a5fa34b99de6.png "023.png")

手順は、

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

となります。

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

![027.png](https://qiita-image-store.s3.amazonaws.com/0/43772/3e6bd073-f237-3293-59bd-620497625ed6.png "027.png")

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

![028.png](https://qiita-image-store.s3.amazonaws.com/0/43772/90d089e5-0ad9-ac42-75fa-f1beaf49a69b.png "028.png")

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

```csharp: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`を探し、テキスト文字列を設定します。

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

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

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

```csharp: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;
  }
```

使う側のサンプルです。

```csharp: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`クラスに以下の関数を実装します。

```csharp: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);
  }
```

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

```csharp: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](https://qiita-image-store.s3.amazonaws.com/0/43772/41fedfb6-323c-7978-0b7b-bc74ae1566a3.png "000.png")

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