スクリプトからUIを更新しよう【Unityスタートガイド9-4】

UIの部品が作れるようになったら、次はゲームの状況に合わせて表示を変えていきましょう。スコアの数字、HPのバー、押せる・押せないボタン。スクリプトからUIを書き換える方法をご紹介します!

目次

この記事で学べること

  • スクリプトからUIの部品を操作する準備(using と参照)
  • TextMeshProの文字を書き換える方法
  • SliderでHPバーを作る方法
  • Imageの色を変える方法
  • ボタンを押せなくする・スクリプトから処理を登録する方法

前提知識

UIをスクリプトから使う準備

usingを書く

UIの部品を使うスクリプトでは、ファイルの先頭に次のusingを書きます。

using UnityEngine;
using UnityEngine.UI;   // Slider、Image、Button など
using TMPro;            // TextMeshProの文字(TMP_Text)

これを書き忘れると「型が見つからない」というエラーになります。UIを使うスクリプトでは、まずこの3行を書くクセをつけましょう。

Inspectorで参照を設定する

6-9の記事で説明したとおり、ほかのオブジェクトは[SerializeField]で変数を作り、Inspectorでドラッグ&ドロップして設定するのが一番確実です。

[SerializeField] private TMP_Text scoreText;
[SerializeField] private Slider hpBar;
  • 文字はTMP_Text型にしておくと、UIのText (TMP)をドロップできます
  • 設定し忘れて「None」のままだと、NullReferenceExceptionになります

文字を書き換える

スコアを表示する

「ScoreDisplay」という名前でスクリプトを作ります。

using UnityEngine;
using TMPro;

public class ScoreDisplay : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;

    private int score = 0;

    void Start()
    {
        UpdateText();
    }

    // ほかのスクリプトやボタンから呼ぶ
    public void AddScore(int points)
    {
        score += points;
        UpdateText();
    }

    private void UpdateText()
    {
        scoreText.text = "スコア: " + score;
    }
}
  • scoreText.text = 文字列;で、表示が書き換わります
  • 数字が変わったときだけUpdateText()を呼んで書き換えます。Updateで毎フレーム書き換えるのは無駄が多いので避けましょう

残り時間を表示する

using UnityEngine;
using TMPro;

public class TimerDisplay : MonoBehaviour
{
    [SerializeField] private TMP_Text timerText;
    [SerializeField] private float timeLimit = 60f;

    private float timeRemaining;

    void Start()
    {
        timeRemaining = timeLimit;
    }

    void Update()
    {
        if (timeRemaining <= 0f)
        {
            return;
        }

        timeRemaining -= Time.deltaTime;

        if (timeRemaining < 0f)
        {
            timeRemaining = 0f;
            Debug.Log("タイムアップ!");
        }

        int minutes = (int)(timeRemaining / 60f);
        int seconds = (int)(timeRemaining % 60f);
        timerText.text = minutes.ToString("00") + ":" + seconds.ToString("00");
    }
}
  • ToString("00")で、「5」を「05」のように2けたで表示できます
  • 時間のように毎フレーム変わるものは、Updateで書き換えてOKです

SliderでHPバーを作る

Sliderの準備

  • Canvasを右クリック>UI>Sliderで作り、名前をHPBarにする
  • HPバーとして使うので、つまみ(Handle Slide Areaの子のHandle)は削除するか非表示にする
  • SliderのInteractableのチェックを外す(プレイヤーが動かせないようにする)
  • Min Value:一番左のときの値(ふつうは0)
  • Max Value:一番右のときの値(最大HP)
  • Value:今の値

HPバーのスクリプト

「HealthBarUI」という名前でスクリプトを作ります。

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class HealthBarUI : MonoBehaviour
{
    [SerializeField] private Slider hpBar;
    [SerializeField] private TMP_Text hpText;
    [SerializeField] private int maxHp = 100;

    private int hp;

    void Start()
    {
        hp = maxHp;

        // バーの最大値をHPの最大値に合わせる(これを忘れると、初期値の1が最大になってしまう)
        hpBar.minValue = 0;
        hpBar.maxValue = maxHp;

        UpdateUI();
    }

    public void TakeDamage(int damage)
    {
        hp = Mathf.Max(hp - damage, 0);   // 0より小さくしない
        UpdateUI();
    }

    public void Heal(int amount)
    {
        hp = Mathf.Min(hp + amount, maxHp);   // 最大HPより大きくしない
        UpdateUI();
    }

    private void UpdateUI()
    {
        hpBar.value = hp;
        hpText.text = hp + " / " + maxHp;
    }
}
  • maxValueを必ず設定する:Sliderの初期のMax Valueは1なので、HPが100でも「1以上はずっと満タン」に見えてしまいます
  • Mathf.Max(a, b)は大きい方、Mathf.Min(a, b)は小さい方を返します。HPが0〜最大の間に収まるようにしています
  • 試すときは、9-2のようにボタンのOn Click ()にTakeDamage(数値は10など)を登録してみましょう
スライム

HPを減らしても、バーがずっと満タンのまま・・・

フェアリー

maxValueの設定忘れかも!Sliderの最大値は最初1になっているから、HPの最大値に合わせてあげてね

Imageの色を変える

using UnityEngine;
using UnityEngine.UI;

public class StatusIndicator : MonoBehaviour
{
    [SerializeField] private Image statusImage;

    public void SetDanger(bool isDanger)
    {
        statusImage.color = isDanger ? Color.red : Color.green;
    }

    public void SetAlpha(float alpha)
    {
        // 色はそのままで、透明度(a)だけ変える
        Color color = statusImage.color;
        color.a = alpha;
        statusImage.color = color;
    }
}
  • image.colorで色を変えられます
  • 透明度だけ変えたいときは、一度色を変数に入れてaを書き換えてから戻します(statusImage.color.a = 0.5f;とは直接書けません)

ボタンを操作する

押せる・押せないを切り替える

using UnityEngine;
using UnityEngine.UI;

public class ShopButton : MonoBehaviour
{
    [SerializeField] private Button buyButton;
    [SerializeField] private int price = 100;

    public void UpdateButton(int money)
    {
        // お金が足りるときだけ押せる
        buyButton.interactable = money >= price;
    }
}

interactableをfalseにすると、ボタンがグレーになって押せなくなります。

スクリプトから押したときの処理を登録する

9-2ではInspectorのOn Click ()で登録しましたが、スクリプトからも登録できます。

using UnityEngine;
using UnityEngine.UI;

public class ButtonSetup : MonoBehaviour
{
    [SerializeField] private Button startButton;

    void Start()
    {
        // 押されたらOnStartを呼ぶ
        startButton.onClick.AddListener(OnStart);
    }

    void OnDestroy()
    {
        // 片付け:登録した処理を外す
        startButton.onClick.RemoveListener(OnStart);
    }

    private void OnStart()
    {
        Debug.Log("スタート!");
    }
}
  • onClick.AddListener(メソッド名)で、押されたときに呼ぶメソッドを登録します(カッコは付けずに名前だけ書きます)
  • スクリプトから登録する場合は、メソッドはprivateでもOKです

実践:スコアとレベルの表示

ボタンを押すとスコアが増えて、100点ごとにレベルが上がり、次のレベルまでのバーが伸びる仕組みを作ってみましょう。

UIの準備

  • ScoreText(Text – TextMeshPro):スコアの表示
  • LevelText(Text – TextMeshPro):レベルの表示
  • ExpBar(Slider):次のレベルまでのバー。Interactableのチェックを外し、つまみを非表示に
  • AddScoreButton(Button – TextMeshPro):テスト用のボタン。文字は「スコア +10」

スクリプト

「ScoreSystem」という名前でスクリプトを作り、空のゲームオブジェクト(名前はGameUI)にアタッチします。

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class ScoreSystem : MonoBehaviour
{
    [SerializeField] private TMP_Text scoreText;
    [SerializeField] private TMP_Text levelText;
    [SerializeField] private Slider expBar;
    [SerializeField] private Button addScoreButton;
    [SerializeField] private int pointsPerLevel = 100;   // 何点ごとにレベルが上がるか
    [SerializeField] private int pointsPerClick = 10;

    private int score = 0;

    void Start()
    {
        expBar.minValue = 0;
        expBar.maxValue = pointsPerLevel;
        addScoreButton.onClick.AddListener(OnAddScoreClicked);
        UpdateUI();
    }

    void OnDestroy()
    {
        addScoreButton.onClick.RemoveListener(OnAddScoreClicked);
    }

    private void OnAddScoreClicked()
    {
        int oldLevel = GetLevel();
        score += pointsPerClick;

        if (GetLevel() > oldLevel)
        {
            Debug.Log("レベルアップ! Level " + GetLevel());
        }

        UpdateUI();
    }

    // スコアからレベルを計算する(0〜99点でレベル1、100〜199点でレベル2…)
    private int GetLevel()
    {
        return score / pointsPerLevel + 1;
    }

    private void UpdateUI()
    {
        scoreText.text = "スコア: " + score;
        levelText.text = "レベル: " + GetLevel();

        // 今のレベルの中で、何点たまっているか(0〜99)
        expBar.value = score % pointsPerLevel;
    }
}

このスクリプトのポイント:

  • レベルはスコアから計算する:score / pointsPerLevel + 1。int同士の割り算は小数点以下が切り捨てられるので、150 / 100 は1になります
  • バーは「今のレベルの中での点数」:score % pointsPerLevel(%は割った余り)。150点なら50で、バーが半分まで伸びます
  • レベルを別の変数で持たずに毎回計算しているので、数字がずれる心配がありません

InspectorでScoreSystemの各欄にUIをドラッグ&ドロップして再生し、ボタンを押してみましょう。

よくあるエラーと対処法

エラー:The type or namespace name ‘TMP_Text’ / ‘Slider’ could not be found

  • 原因:using TMPro;やusing UnityEngine.UI;を書き忘れている
  • 対処法:ファイルの先頭にusingを追加する

エラー:NullReferenceException

  • 原因:Inspectorで参照を設定し忘れている(「None」のまま)
  • 対処法:Inspectorで、UIの部品をドラッグ&ドロップで設定する

エラー:Sliderのバーが動かない・ずっと満タン

  • 原因:maxValueがHPなどの最大値に合っていない(初期値は1)
  • 対処法:StartでmaxValueを設定する。Inspectorで設定してもOK

エラー:文字の欄にText (TMP)をドロップできない

  • 原因:変数の型が古いText型になっている
  • 対処法:using TMPro;を書き、変数をTMP_Text型にする

エラー:ボタンを押しても反応しない

  • 原因:EventSystemがない、またはAddListenerが呼ばれていない
  • 対処法:EventSystemを確認する。Startでの登録がエラーで止まっていないか、Consoleを確認する

まとめ

  • UIを使うスクリプトにはusing UnityEngine.UI;とusing TMPro;
  • 参照は[SerializeField]でInspectorから設定
  • 文字:text、Slider:maxValueとvalue、Image:color、Button:interactableとonClick.AddListener
  • 表示は、値が変わったときだけ書き換える

次のステップ

次は、プレイヤーが文字を入力したり、スライダーで音量を変えたりする「入力系」のUIを学びましょう:

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次