TD-ディフェンダーの設置と重複確認-part10

作成したスタンプを利用して、狙った位置にディフェンダーを配置しましょう。また、同じ位置に作れないように重複確認も行います。重複確認にはDictionaryを使いますよ。

目次

ディフェンダーの設置

作成したスタンプを利用して、ディフェンダーを設置します。本来はどのキャラを作るか選択する必要がありますが、ここでは一旦指定したキャラを設置するのみとします。

設置用のスクリプト修正

今回の変更のポイントは以下に注目しながら変更点を確認してください。

  • Defenderをインスタンス化するためのプレファブをインスペクターでセットできるようにする
  • 左クリックで現在表示されている位置にインスタンスを生成する
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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

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

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

        var spriteRenderer = GetComponent<SpriteRenderer>();
        spriteRenderer.sprite = defenderData.unitSprite;
    }

    private void Update()
    {
        // マウスの位置を追従する
        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))
        {
            // 左クリックされたら、DefenderControllerを生成する
            var defender = Instantiate(defenderPrefab, transform.position, Quaternion.identity);
            defender.Initialize(defenderData);
        }
    }
}

ディフェンダーのプレファブ化

このタイミングでディフェンダーをプレファブ化します。ヒエラルキーに作成済みのDefenderをAssetsPrefabsフォルダにドラッグアンドドロップして、プレファブにしてください。

プレファブにしたらヒエラルキーから削除しましょう

コンポーネントの設定

DefenderStampゲームオブジェクトのインスペクターを開き、先程さくせいしたDefenderのプレファブをセットします。コンポーネントを指定したプレファブなので、Defender以外はセット出来ないようになってます。

設置のテスト

これにてディフェンダーを設置することができるようになりました。実際に試してみましょう。ゲームを動かして画面をクリックするとスタンプの位置にディフェンダーを配置することが出来ますよ。

ただ、今のままでは同じ場所にも配置が出来てしまいます。ということで、次は同じ場所にディフェンダーを配置出来ない処理を追加します。

同じ位置にディフェンダーを配置出来ないようにする

設置中のディフェンダーの管理は、本当なら別のスクリプトで行いたいところですが、今回はDefenderStampに任せます。

どの場所にディフェンダーが設置されているかを登録する

どの場所にディフェンダーが存在するかを知るには、設置時にその場所を組み合わせた情報としてDictionaryに登録します。イメージとしては将棋の「2四歩」と言われたら、下図の赤いところに歩があることを示します。

日本将棋連盟様より引用

今回はグリッド状に配置を行うため、Vector2Intとディフェンダーの組み合わせでどこに何がいるかを登録したいと思います。今回は変化のある部分のみプログラムを載せます。全部出来たのは最後に。

public class DefenderStamp : MonoBehaviour
{
    // ************追加箇所************
    // 登録済みのディフェンダーを調べるためのキャッシュ
    private Dictionary<Vector2Int, DefenderController> defenderMap = new Dictionary<Vector2Int, DefenderController>();

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // 左クリックされたら、DefenderControllerを生成する
            var defender = Instantiate(defenderPrefab, transform.position, Quaternion.identity);
            defender.Initialize(defenderData);

            // ************追加箇所************
            // 生成したDefenderControllerを保持する
            defenderMap.Add(gridPosition, defender);
        }
    }
}

ここでの処理は登録をしているだけなので、まだ重複で配置出来ます。ただしDictionaryは同じKey(今回だとgridPosition)が同じものを登録しようとすると、すでにはいってんよーってエラーが発生します。

重複登録を行わないための判定処理を追加

実装する内容としては次のようなものを想定します。コスト計算などを行う時のことを考慮して、どこに何を作ったかを通知するイベントも発信します。下記は、今の状態でのプログラム全部になります。

  • ディフェンダーを追加する前に、対象の位置(gridPosition)の空きを確認
  • 空きがあれば生成を行う
  • ついでに作ったことを誰かに伝えるためのイベントも発信する
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(defenderData);
    }

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

        var spriteRenderer = GetComponent<SpriteRenderer>();
        spriteRenderer.sprite = defenderData.unitSprite;
    }

    private void Update()
    {
        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("すでにディフェンダーがいるよ!");
            }
        }
    }
}

ログはなくても良かったが、わかりやすくするために入れておきます。この状態で同じ場所にディフェンダーを作ろうとして見てください。既に存在する場合はログが表示されます。

Dictionaryは登録の手間や、型に注意を払う必要がありますが、シンプルなリストの検索などに比べると圧倒的に早いです。大量のデータなどを扱う場合にその真価を発揮します。うまく使い分けができるようにしておきましょう。

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

この記事を書いた人

コメント

コメントする

目次