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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

4509閲覧

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

irohashi

総合スコア9

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2020/07/18 23:33

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

以下詳細です。

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

c#

1public class MyTileBase : RuleTile 2 { 3 protected TilePutObjects _tile_put_objects; 4 ... 5 }

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

c#

1protected void Move(Vector3Int grid_add) 2 { 3 var tilemap = From.ReferenceTilemap; 4 var cur_position = new Vector3Int(From.CurGridPosition.x, From.CurGridPosition.y, From.CurGridPosition.z); 5 var next_grid = From.CurGridPosition + grid_add; 6 7 var next_tile_base = tilemap.GetTile<MyTileBase>(next_grid); 8 var cur_tile_base = tilemap.GetTile<MyTileBase>(cur_position); 9 10 if (!next_tile_base || !next_tile_base.Type.Movable || !next_tile_base.CheckMovable()) return; 11 From.gameObject.transform.position = tilemap.GetCellCenterWorld(next_grid); 12 From.CurGridPosition = next_grid; 13 14 next_tile_base.PutObjects.Add(From); 15 cur_tile_base.PutObjects.Remove(From); 16 }

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

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

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

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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

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

イメージ説明

csharp

1/// <summary> 2/// タイルにデータを保存するためのクラス 3/// </summary> 4public class TileWithData 5{ 6 /// <summary>タイルの Position。キーとなるデータであるため必須。</summary> 7 public Vector3 WorldPosition { get; set; } 8 /// <summary>クリックした回数。保存したいカスタムデータ。</summary> 9 public int ClickCount { get; set; } 10}

csharp

1using System.Collections.Generic; 2using UnityEngine; 3using UnityEngine.Tilemaps; 4 5/// <summary> 6/// Tilemap(コンポーネントと同じ)オブジェクトにアタッチする。各タイルにデータを保存する機能を提供する。 7/// </summary> 8[RequireComponent(typeof(Tilemap))] 9public class TilemapExtension : MonoBehaviour 10{ 11 /// <summary> 12 /// タイルのデータベース 13 /// </summary> 14 public Dictionary<Vector3, TileWithData> TileData 15 { 16 get { return this.m_tileDatabase; } 17 } 18 19 /// <summary>タイルのデータベース</summary> 20 Dictionary<Vector3, TileWithData> m_tileDatabase; 21 Tilemap m_tileMap; 22 23 void Start() 24 { 25 m_tileMap = GetComponent<Tilemap>(); 26 InitTileData(); 27 } 28 29 /// <summary> 30 /// タイルのデータベースをセットアップする 31 /// </summary> 32 private void InitTileData() 33 { 34 m_tileDatabase = new Dictionary<Vector3, TileWithData>(); 35 36 foreach (Vector3Int pos in m_tileMap.cellBounds.allPositionsWithin) 37 { 38 var localPlace = new Vector3Int(pos.x, pos.y, pos.z); 39 40 if (!m_tileMap.HasTile(localPlace)) 41 { 42 continue; 43 } 44 45 var tile = new TileWithData 46 { 47 WorldPosition = m_tileMap.CellToWorld(localPlace), 48 }; 49 50 m_tileDatabase.Add(tile.WorldPosition, tile); 51 } 52 } 53}

csharp

1using UnityEngine; 2 3/// <summary> 4/// 適当なオブジェクトにアタッチして使う。 5/// タイルをクリックしたらそのタイルにクリックした回数を保存する。 6/// </summary> 7public class TestStoringDataInTiles : MonoBehaviour 8{ 9 [SerializeField] TilemapExtension m_tileMap; 10 11 void Update() 12 { 13 if (Input.GetMouseButtonUp(0)) 14 { 15 Vector3 point = Camera.main.ScreenToWorldPoint(Input.mousePosition); 16 var worldPoint = new Vector3Int(Mathf.FloorToInt(point.x), Mathf.FloorToInt(point.y), 0); 17 18 // クリックした所にあるタイルにクリックした回数を保存する。 19 TileWithData tile; 20 if (m_tileMap.TileData.TryGetValue(worldPoint, out tile)) 21 { 22 tile.ClickCount += 1; 23 Debug.LogFormat("このタイルは {0} 回クリックした", tile.ClickCount.ToString()); 24 } 25 } 26 } 27 28 private void Start() 29 { 30 if (!m_tileMap) 31 { 32 Debug.LogError("Tilemap が設定されていません。"); 33 } 34 } 35}

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

投稿2020/07/24 22:06

編集2020/07/24 22:09
bboydaisuke

総合スコア5308

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bboydaisuke

2020/07/24 23:38 編集

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

2020/07/28 03:08

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問