TD-ボタンでスタンプ選択中のディフェンダーを切り替える-part11

ディフェンダーの設置はできるようになりましたが、今のままだとインスペクターでセットされたものしか対応出来ません。今回はUIのボタンを利用して切り替えられるようにしましょう。

目次

ボタンの作成

まずは選択するための準備として、ボタンを作成します。ここではボタンのみの作成で、スタンプ機能との連携はそのあと行います。

ビューを作成

UIでボタンを作成します。今回はレイアウトグループなどは使わずに単純に配置だけ行います。1つだけ作成してから完成したものを複製して、最終的には2個のボタンを作成します。

  • ボタンの作成
    • Canvas右クリック>UI>Button – Text Mesh Proを作成
      • 追加時にダイアログが表示される場合、Import TMP Essensialsを押してください。
      • ウインドは消えないのでインポート出来たら閉じる。(下のボタンは無視してオッケー)
    • 名前をStampSelectButtonに変更
  • 位置や大きさを変更
    • PosX:-100 PosY:-550
    • Width:150 Height:150
  • テキストのAlignment変更
    • ボタンの下にあるTest(TMP)のAlignmentを変更
    • 左右方向:中央・上下方向:下

ボタンのテキストは、ディフェンダーのコストの表示に利用します。

スクリプト作成:StampSelectButton

作ったボタンと連携するスクリプトを作成しましょう。実装したい機能としては次のようなものがあります。

  • どのディフェンダーなのかをセットできる:インスペクターでセット
  • ディフェンダーのアイコンを表示させる
  • コストを表示させる
  • どのディフェンダーが選択されたかを通知する:イベント系

スクリプト名はStampSelectButtonで作成します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using TMPro;

[RequireComponent(typeof(Button), typeof(Image))]
public class StampSelectButton : MonoBehaviour
{
    [SerializeField] private DefenderData defenderData;
    public static UnityEvent<DefenderData> OnAnyDefenderSelect = new UnityEvent<DefenderData>();

    private void Start()
    {
        Initialize(defenderData);
    }

    public void Initialize(DefenderData defenderData)
    {
        this.defenderData = defenderData;

        var button = GetComponent<Button>();
        button.onClick.RemoveAllListeners();
        button.onClick.AddListener(() =>
        {
            OnAnyDefenderSelect.Invoke(defenderData);
        });

        var image = GetComponent<Image>();
        image.sprite = defenderData.IconSprite;

        var text = GetComponentInChildren<TextMeshProUGUI>();
        if (text != null)
        {
            text.text = $"Cost:" + defenderData.cost.ToString();
        }
    }
}

コンポーネントのセット

スクリプトの準備が出来たら実装していきます。

  • StampSelectButtonゲームオブジェクトにスクリプトをアタッチ
  • インスペクターのDefenderDataに好きなキャラをセット

動かして確認してみる&2つ目のボタンを作成

あとはゲームを動かすと、アイコン画像が表示されたら成功です。1つのボタンが作成出来たらもう一つボタンを作成しましょう。

  • StanmSelectButtonを右クリック>Duplicate(複製)
    • 選択状態でCtrl+DでもOK
  • 新しく作られたボタンの位置を変更
    • X:100
  • DefenderDataを変更
    • 最初に作ったキャラと別のキャラをセット

表示が更新されたら成功です。ボタンのアイコンにフレームが無いので、気になる人はアイコン素材を別に用意してみてください。

ボタンとスタンプを連携させる

さて、試しにボタンをクリックした方はいらっしゃると思います。ボタンは反応するけどキャラは変わらないし、ボタンの後ろ側にスタンプが押されてしまう。ということで、ここからはゲームで利用できる様に処理を追加していきます。

スタンプのキャラを切り替える

まずはボタン本来の機能として、スタンプのキャラクターを反映させる処理を実装します。ボタンにはstaticなイベント(OnAnyDefenderSelect)として押されたボタンに設定されているキャラが誰なのかを発信するイベントが実装されています。したがって、ボタン側での実装ではなく、スタンプ側が聞き耳を立てることで対応可能です。DefenderStampのスクリプトを変更しますが、次のような機能を実装します。

  • ボタンが押されたらスタンプのキャラを表示
    • 選択中にスタンプ設置ができるようになる
  • 右クリックを行うと選択を解除(スタンプが非表示になる)
    • 非表示状態ではスタンプによる設置を出来ないようにする
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(SpriteRenderer))]
public class DefenderStamp : MonoBehaviour
{
    [SerializeField] private DefenderData defenderData;
    [SerializeField] private DefenderController defenderPrefab;

    private Dictionary<Vector2Int, DefenderController> defenderMap = new Dictionary<Vector2Int, DefenderController>();

    public UnityEvent<Vector2Int, DefenderController> OnDefenderCreated = new UnityEvent<Vector2Int, DefenderController>();

    private void Start()
    {
        // はじめは未選択状態にする
        Initialize(null);

        // スタンプボタンが選択されたら、スタンプを切り替える
        StampSelectButton.OnAnyDefenderSelect.AddListener((defenderData) =>
        {
            Initialize(defenderData);
        });
    }

    private void Initialize(DefenderData defenderData)
    {
        this.defenderData = defenderData;

        var spriteRenderer = GetComponent<SpriteRenderer>();
        if (defenderData == null)
        {
            spriteRenderer.sprite = null;
        }
        else
        {
            spriteRenderer.sprite = defenderData.unitSprite;
        }
    }

    private void Update()
    {
        if (defenderData == null)
        {
            return;
        }

        Vector3 mousePos = Input.mousePosition;
        Vector3 targetPosition = Camera.main.ScreenToWorldPoint(mousePos);

        Vector2Int gridPosition = Vector2Int.RoundToInt(targetPosition);
        transform.position = new Vector3(gridPosition.x, gridPosition.y, 0.0f);

        if (Input.GetMouseButtonDown(0))
        {
            // グリッドの場所に空きがあるかどうかをチェックする
            if (!defenderMap.ContainsKey(gridPosition))
            {
                var defender = Instantiate(defenderPrefab, transform.position, Quaternion.identity);
                defender.Initialize(defenderData);

                // グリッドの場所にディフェンダーを追加する
                defenderMap.Add(gridPosition, defender);
                OnDefenderCreated.Invoke(gridPosition, defender);
            }
            else
            {
                Debug.Log("すでにディフェンダーがいるよ!");
            }
        }
        else if (Input.GetMouseButtonDown(1))
        {
            // 左クリックでディフェンダーを削除する
            Initialize(null);
        }
    }
}

この状態だと、ボタンをクリックするとスタンプの対象キャラが切り替わります。また右クリックでスタンプのキャラが解除されます。

ボタンの上でクリックしてもスタンプが反応しないようにする

締めにボタンを貫通する設置現象をなんとかします。これはEventSystemというものを使い、マウスカーソル上にどんなオブジェクトが存在しているのかを取得して判別を行います。また、スタンプ自体が表示されていると不自然になるため、スタンプの表示機能を追加します。

今回の、最終的なDefenderStampのソースコードは以下

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

[RequireComponent(typeof(SpriteRenderer))]
public class DefenderStamp : MonoBehaviour
{
    [SerializeField] private DefenderData defenderData;
    [SerializeField] private DefenderController defenderPrefab;

    private Dictionary<Vector2Int, DefenderController> defenderMap = new Dictionary<Vector2Int, DefenderController>();
    public UnityEvent<Vector2Int, DefenderController> OnDefenderCreated = new UnityEvent<Vector2Int, DefenderController>();

    private SpriteRenderer spriteRenderer;

    private void Start()
    {
        // スタンプを表示しているSpriteRendererを取得する
        spriteRenderer = GetComponent<SpriteRenderer>();

        // はじめは未選択状態にする
        Initialize(null);

        // スタンプボタンが選択されたら、スタンプを切り替える
        StampSelectButton.OnAnyDefenderSelect.AddListener((defenderData) =>
        {
            Initialize(defenderData);
        });
    }

    private void Initialize(DefenderData defenderData)
    {
        this.defenderData = defenderData;

        // メンバー変数のspriteRendererを利用するため不要になります
        //var spriteRenderer = GetComponent<SpriteRenderer>();
        if (defenderData == null)
        {
            spriteRenderer.sprite = null;
        }
        else
        {
            spriteRenderer.sprite = defenderData.unitSprite;
        }
    }

    private void Update()
    {
        if (defenderData == null)
        {
            return;
        }
        // マウスの位置がUIの上にある場合は、ユニットを表示しない
        if (EventSystem.current.IsPointerOverGameObject())
        {
            spriteRenderer.enabled = false;
            return;
        }
        else
        {
            spriteRenderer.enabled = true;
        }

        Vector3 mousePos = Input.mousePosition;
        Vector3 targetPosition = Camera.main.ScreenToWorldPoint(mousePos);

        Vector2Int gridPosition = Vector2Int.RoundToInt(targetPosition);
        transform.position = new Vector3(gridPosition.x, gridPosition.y, 0.0f);

        if (Input.GetMouseButtonDown(0))
        {
            // グリッドの場所に空きがあるかどうかをチェックする
            if (!defenderMap.ContainsKey(gridPosition))
            {
                var defender = Instantiate(defenderPrefab, transform.position, Quaternion.identity);
                defender.Initialize(defenderData);

                // グリッドの場所にディフェンダーを追加する
                defenderMap.Add(gridPosition, defender);
                OnDefenderCreated.Invoke(gridPosition, defender);
            }
            else
            {
                Debug.Log("すでにディフェンダーがいるよ!");
            }
        }
        else if (Input.GetMouseButtonDown(1))
        {
            // 左クリックでディフェンダーを削除する
            Initialize(null);
        }
    }
}

実際に動かしてみる

ゲームを動かして、スタンプ機能を確認してみましょう。

  • ゲーム開始時に画面内をクリックしてもディフェンダーが設置されない
  • ボタンを選択するとスタンプに設定したキャラが表示される
  • 左クリックでディフェンダーが設置される
  • 右クリックでスタンプが非表示になる
  • スタンプ表示中にボタンの上にカーソルを合わせると非表示になる(少し分かりづらいかも)
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメント一覧 (1件)

コメントする

目次