質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

UnityのTilemap.GetTileの仕様について

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 235

irohashi

score 9

UnityEngine.Tilemapsを利用して、2Dのターン制ローグライクゲームの作成をしています。
今、Tile上にいるキャラクターが移動する際、その移動先に既に別のオブジェクトがあった場合は移動させないという処理を書いているところです。
そこで、GetTile関数が私が想定していた動作と別の動きをしていて、困っています。

以下詳細です。

以下のコードは、RuleTileを継承して各TileにTilePutObjectsというリストを持たせるためのものです。
このTilePutObjectsに、Tileごとに上に乗っているオブジェクトを保存して管理させる狙いです。

public class MyTileBase : RuleTile
    {
        protected TilePutObjects _tile_put_objects;
        ...
    }

以下のコードは、上記のコードにより、移動先のTileに既にオブジェクトがあった場合、移動せず、無い場合は移動するというコードです。

protected void Move(Vector3Int grid_add)
        {
            var tilemap = From.ReferenceTilemap;
            var cur_position = new Vector3Int(From.CurGridPosition.x, From.CurGridPosition.y, From.CurGridPosition.z);
            var next_grid = From.CurGridPosition + grid_add;

            var next_tile_base = tilemap.GetTile<MyTileBase>(next_grid);
            var cur_tile_base = tilemap.GetTile<MyTileBase>(cur_position);

            if (!next_tile_base || !next_tile_base.Type.Movable || !next_tile_base.CheckMovable()) return;
            From.gameObject.transform.position = tilemap.GetCellCenterWorld(next_grid);
            From.CurGridPosition = next_grid;

            next_tile_base.PutObjects.Add(From);
            cur_tile_base.PutObjects.Remove(From);
        }

以下は、MyTileBaseを継承した三種類のTileを配置したものになります。
大雑把に、黒のタイル、灰色のタイル、白のタイルとします。
イメージ説明

以上の状態で、キャラクターの移動先のTileを取得(tilemap.GetTile)しようとすると、私の想定ではその移動先のTileが保存しているTilePutObjectsが取得できるはずでした。

しかし実際は、例えば移動先が白のタイルの場合、その白のタイル全体でTilePutObjectsが保存されていて、そのTilePutObjectsが返されます。

例えば、座標(0,0)が白のタイルで、TilePutObjectsに既にオブジェクトが登録されているとします。
その状態で、座標(2,2)に移動するとします。移動先が白のタイルの場合、移動先にオブジェクトが既にあると判定されて、移動することができません。

つまり、黒のタイル、灰色のタイル、白のタイルのグループでTilePutObjectsが管理されていることになっています。なので白のタイルのいずれかにオブジェクトがある場合、移動先がどれだけ離れていようとも、白のタイルには移動できません。

上記で上げた画像では白と灰色のタイルが交互に配置されています。この状態で既にオブジェクトが配置されている場合、斜め移動でしかできない状態です。

以上の動作は、Tilemap.GetTileで取得できるものが、その座標にあるTileのグループを取得するものだからなのでしょうか。
各Tileの個別に、状態を保存する変数を持たせるにはどうしたら良いでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

RuleTile は用途が違うのでこういうことには使えないですね。

各Tileの個別に、状態を保存する変数を持たせるにはどうしたら良いでしょうか。

データ保存の例として「各タイルに "クリックされた回数" を保存する」ということをやってみました。

イメージ説明

/// <summary>
/// タイルにデータを保存するためのクラス
/// </summary>
public class TileWithData
{
    /// <summary>タイルの Position。キーとなるデータであるため必須。</summary>
    public Vector3 WorldPosition { get; set; }
    /// <summary>クリックした回数。保存したいカスタムデータ。</summary>
    public int ClickCount { get; set; }
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

/// <summary>
/// Tilemap(コンポーネントと同じ)オブジェクトにアタッチする。各タイルにデータを保存する機能を提供する。
/// </summary>
[RequireComponent(typeof(Tilemap))]
public class TilemapExtension : MonoBehaviour
{
    /// <summary>
    /// タイルのデータベース
    /// </summary>
    public Dictionary<Vector3, TileWithData> TileData
    {
        get { return this.m_tileDatabase; }
    }

    /// <summary>タイルのデータベース</summary>
    Dictionary<Vector3, TileWithData> m_tileDatabase;
    Tilemap m_tileMap;

    void Start()
    {
        m_tileMap = GetComponent<Tilemap>();
        InitTileData();
    }

    /// <summary>
    /// タイルのデータベースをセットアップする
    /// </summary>
    private void InitTileData()
    {
        m_tileDatabase = new Dictionary<Vector3, TileWithData>();

        foreach (Vector3Int pos in m_tileMap.cellBounds.allPositionsWithin)
        {
            var localPlace = new Vector3Int(pos.x, pos.y, pos.z);

            if (!m_tileMap.HasTile(localPlace))
            {
                continue;
            }

            var tile = new TileWithData
            {
                WorldPosition = m_tileMap.CellToWorld(localPlace),
            };

            m_tileDatabase.Add(tile.WorldPosition, tile);
        }
    }
}
using UnityEngine;

/// <summary>
/// 適当なオブジェクトにアタッチして使う。
/// タイルをクリックしたらそのタイルにクリックした回数を保存する。
/// </summary>
public class TestStoringDataInTiles : MonoBehaviour
{
    [SerializeField] TilemapExtension m_tileMap;

    void Update()
    {
        if (Input.GetMouseButtonUp(0))
        {
            Vector3 point = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            var worldPoint = new Vector3Int(Mathf.FloorToInt(point.x), Mathf.FloorToInt(point.y), 0);

            // クリックした所にあるタイルにクリックした回数を保存する。
            TileWithData tile;
            if (m_tileMap.TileData.TryGetValue(worldPoint, out tile))
            {
                tile.ClickCount += 1;
                Debug.LogFormat("このタイルは {0} 回クリックした", tile.ClickCount.ToString());
            }
        }
    }

    private void Start()
    {
        if (!m_tileMap)
        {
            Debug.LogError("Tilemap が設定されていません。");
        }
    }
}

参考にした記事: Unity Tilemaps and Storing Individual Tile Data

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/25 08:38 編集

    このコードは Grid コンポーネントの Cell Size が 1x1 であることが前提です。そうでない場合はそれを考慮して修正してください。

    キャンセル

  • 2020/07/28 12:08

    ありがとうございます。
    やはりDictionaryにVectorIntをkeyにして管理するしかないですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る