さぁ、UnityのTilemapを始めよう！4/8 ~Tile について編~

「[さぁ、UnityのTilemapを始めよう！](https://qiita.com/RyotaMurohoshi/items/5c3cdd24b7e09bfd8ed3)」シリーズ、全8回の内の4回目です。

本シリーズの「[第2回 とにかくまずは触ってみよう編](https://qiita.com/RyotaMurohoshi/items/f386e81b5e2ea1ffe57d)」では、SpriteをTilePaletteウィンドウにドラッグアンドドロップして、Tileアセットを作成しました。ところでTileアセットは一体どういうものなのでしょうか。今回は、Tile型とTileアセットについての説明をします。カスタムTileの挙動を知りたい人、作成したい人はこの回を読んでください。

前回は、「[第3回 Spriteの設定編](https://qiita.com/RyotaMurohoshi/items/718e2cceec3b6da28224)」です。

# Tileアセット関連要素の紹介

Tileアセットは「UnityEngine.Tilemaps.Tile」型のScriptableObjectです。これに関連する型は次のとおりです。

* UnityEngine.ScriptableObject
* UnityEngine.Tilemaps.TileBase
* UnityEngine.Tilemaps.Tile
* ユーザー定義のカスタムTileクラス

「UnityEngine.ScriptableObject」についてはご存知のとおり、Unityプロジェクトにおけるデータストアとして便利なクラスです。

「UnityEngine.Tilemaps.TileBase」は「UnityEngine.ScriptableObject」を継承しています。「UnityEngine.Tilemaps.TileBase」は後述する4つのvirtualメソッドをもつ抽象クラスです。（abstractメソッドではなくて、virtualメソッドです）。抽象クラスですので、このクラスのインスタンス・ScriptableObjectを直接作ることはできません。継承したクラスをつくり、そのインスタンス・ScriptableObjectを作成する必要があります。

「UnityEngine.Tilemaps.Tile」は、「UnityEngine.Tilemaps.TileBase」を継承した具象クラスです。そして、「[第2回 とにかくまずは触ってみよう編](https://qiita.com/RyotaMurohoshi/items/f386e81b5e2ea1ffe57d)」で作成したTileアセットはこのクラスのScriptableObjectです。

ユーザー定義のカスタムTileクラスを作るために、「UnityEngine.Tilemaps.TileBase」を継承した具象クラスを作成します。そして作りたい挙動を実現するために、適切に「UnityEngine.Tilemaps.TileBase」の4つのvirtualメソッドを実装します。

# 関連する型

Tileクラスの仕組みを理解するうえで把握しておきたい型を2つ紹介します。

ひとつめの型は「UnityEngine.Tilemaps.TileData」という型です。これはTileの色や姿勢などの情報をつかさどる構造体です。

この型はプロジェクト内で管理するアセット（ScriptableObject）ではないということに注意してください。TileDataは、実行時・エディターのプレビュー中に、メソッド間でやりとりされる「データ」です。TileDataは次のデータを扱います。

* Sprite
* 色
* 姿勢
* ユーザーが色や姿勢を変更できるかどうかをつかさどるフラグ
* Tileに関連するのゲームオブジェクトのプレハブ

一番最後のプレハブを設定すると、自動的にゲームオブジェクトが生成されます。後の節のTileBaseクラスのStartUpも参照してください。

詳細は次のリンクを参照してください。

* https://docs.unity3d.com/ScriptReference/Tilemaps.TileData.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-TileData.html

ふたつめの型は「UnityEngine.Tilemaps.ITilemap」という型です。

この型はコンポーネントである「UnityEngine.Tilemaps.Tilemap」というクラスとは別の型であることに注目してください。「Tilemap」はコンポーネントで、「ITilemap」はコンポーネントではありません。また、型名の先頭が「I」で始まっていますが、インターフェースではなくクラスであるということにも気を付けてください。

「ITilemap」も「TileData」と同様に、実行時・エディターのプレビュー中に、メソッド間でやりとりされる「データ」です。ITileMapには、Tilemapコンポーネントの位置や全体の境界をつかさどるプロパティや、Vector3Int型のポジションを引数として渡しそのポジションのセルの情報を取得する各種メソッドを持ちます。

詳細は次のリンクを参照してください。

* https://docs.unity3d.com/ScriptReference/Tilemaps.ITilemap.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-OtherClasses.html

# TileBaseクラスのvirtualメソッド

Tileを扱う上でキーとなるTileBaseクラス。このTileBaseクラスの4つのvirtualメソッドを紹介します。

ここで紹介するメソッドは、ユーザーが呼び出すためのメソッドではありません。MonoBehaviourクラスのStartメソッドやUpdateメソッドのように、ランタイムから呼び出されるメソッドであることに注意してください。

この節で紹介するメソッドを理解することは、ユーザー定義のカスタムTileクラスを作るために非常に大切です。

---

1つ目は「GetTileData」です。

GetTileDataメソッドのシグニチャは次のとおりです。

```csharp
public void GetTileData(
  Vector3Int position,
  ITilemap tilemap,
  ref TileData tileData
)
```

このメソッドは、第一引数「position」のセルに、どのようにタイルを表示するかを決定するためのメソッドです。返り値はvoid型なのに注目してください。表示するTileの情報を、第三引数のTileData型の「tileData」のプロパティを更新することで設定します。TileData型のプロパティに表示したいSprite・色・姿勢などを設定します。

また、第二引数のITilemap型の引数の情報を利用することが可能です。これにより、対象のセルの隣接するセルの情報をつかって表示するSpriteを決定するということも可能です。

GetTileDataメソッドは、Tileを設置したとき、そしてプレビュー中にセルを置こうとカーソルを移動させたときにも呼ばれます。そして「ITilemap.RefreshTile（Vector3Int）」メソッドが呼ばれた際も呼ばれます。

* https://docs.unity3d.com/ScriptReference/Tilemaps.TileBase.GetTileData.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-TileBase.html

---

2つ目は「RefreshTile」です。

RefreshTileメソッドのシグニチャは次のとおりです。

```csharp
public void RefreshTile(
  Vector3Int location,
  ITilemap tileMap
)
```

RefreshTileメソッドは、タイルを置いたとき・削除されたときに呼ばれます。またPreview中も同様です。

隣接Tileの変更にあわせて表示するSpriteを変えるカスタムTileでは、このメソッドが非常に重要です。実際`2d-extras`の`RuleTile`、`PipelineTile`、`TerrainTile`などでは、このメソッドが活用されています。

* https://docs.unity3d.com/ScriptReference/Tilemaps.TileBase.RefreshTile.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-TileBase.html

---

3つ目は「GetTileAnimationData」です。

GetTileAnimationDataメソッドのシグニチャは次のとおりです。

```csharp
public bool GetTileAnimationData(
  Vector3Int position,
  Tilemaps.ITilemap tilemap,
  ref Tilemaps.TileAnimationData tileAnimationData
)
```

GetTileAnimationDataメソッドは、その名のとおりアニメーションのためのメソッドです。アニメーションをする必要があるならば返り値に「true」を、必要がないならば「false」を返します。「true」を返しアニメーションをする際は、第三引数のtileAnimationDataに表示するSpriteやスピードを設定します。

実際のコードは、`2d-extras`の`AnimatedTile`を参照してください。

* https://docs.unity3d.com/ScriptReference/Tilemaps.TileBase.GetTileAnimationData.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-TileBase.html

---

4つ目は「StartUp」です。

StartUpメソッドのシグニチャは次のとおりです。

```csharp
public bool StartUp(
  Vector3Int position,
  Tilemaps.ITilemap tilemap,
  GameObject go
)
```

注目してほしいのは、第三引数のGameObject型です。これはGetTileDataメソッドのTileDataのプロパティ「gameObject」に設定したプレハブをもとに生成されたGameObjectが、引数としてわたってきます。GetTileDataメソッドがStartUpメソッドの前に呼ばれることに注意してください。

* https://docs.unity3d.com/ScriptReference/Tilemaps.TileBase.StartUp.html
* https://docs.unity3d.com/Manual/Tilemap-ScriptableTiles-TileBase.html

# Tileアセットの作成方法

次にTileアセットの作成方法を紹介します。

「[第2回 とにかくまずは触ってみよう編](https://qiita.com/RyotaMurohoshi/items/f386e81b5e2ea1ffe57d)」では、SpriteをTilePaletteウィンドウにドラッグアンドドロップして、Tileアセットを作成する方法を紹介しました。これ以外の作成方法も存在します。

ProjectウィンドウのCreateタブから、「Tile」を選択し作成する方法です。作成されたTileアセットに対して、Spriteの設定やColorの設定します。この方法でも作成はできますが、筆者は、SpriteをTilePaletteウィンドウにドラッグアンドドロップする方法をお勧めします。

# PipelineTileについて

ユーザー定義のカスタムTileクラスの例であるPipelineTileについて簡単に説明します。

これは、`2d-extras`の中にあるカスタムTileのひとつです。（`2d-techdemos`の中にも同様のものがあります。）PilelineTileは隣接するTileの設置状況により表示するTileのSprite・姿勢が変わるTileの一種です。次の画像はシーンにひとつだけ、PipelineTileを配置した画像です。

![pipeline_0.png](https://qiita-image-store.s3.amazonaws.com/0/4125/b7590a5e-901f-e415-f32a-fbb0467feef3.png)

次の画像はシーンに先に置いたTileの隣に、PipelineTileを追加で配置した画像です。

![pipeline_1.png](https://qiita-image-store.s3.amazonaws.com/0/4125/39fdce39-be54-85b5-5122-a7da0a7fd8be.png)

先に置いていたTileの画像が変わったことに気がつきましたか。PipelineTileは隣接するTileの設置状況により表示するTileのSprite・姿勢が変わるTileです。先に置いてあったTileのとなりに新たにTileが置かれたことにより、TileのSprite・姿勢が変わりました。

PipeleineTileはカスタムTileです。Tileですので、TileBaseクラスを継承しています。このようなPipelineのロジックは、TileBaseクラスのvirtualメソッドを駆使すれば比較的簡単に実装することができます。ポイントは3点です。

* 5状態の表示すべきSpriteをScriptableObjectとして保持する
* GetTileDataメソッドで、上下左右の隣接するTileの存在を見て、表示すべきSpriteと姿勢を決定する
* RefreshTileメソッドで、隣接するTileの更新を促すため`ITielmap.RefreshTile（Vector3Int）`メソッドを呼ぶ

詳しくはコードを見てください。筆者のお勧めはTileBaseクラスの4つのvirtualメソッドの仕様をさらっと確認し、Pipelineクラスのコードを読んで、もう一度TileBaseクラスの4つのvirtualメソッドの仕様をがっつり理解するという手順です。カスタムTileへの理解が深まるでしょう。

また、`RuleTile`と`PipelineTile`のロジックのコアの部分が共通です。これらのクラスのコードを読んでみることもお勧めです。

# 次回とシリーズ一覧

次回は、「[第5回 Tilemap関連のコンポーネント編](https://qiita.com/RyotaMurohoshi/items/ae46343828634a2d3321)」です。

* [第1回 概要編](https://qiita.com/RyotaMurohoshi/items/5c3cdd24b7e09bfd8ed3)
* [第2回 とにかくまずは触ってみよう編](https://qiita.com/RyotaMurohoshi/items/f386e81b5e2ea1ffe57d)
* [第3回 Spriteの設定編](https://qiita.com/RyotaMurohoshi/items/718e2cceec3b6da28224)
* [第4回 Tile について編](https://qiita.com/RyotaMurohoshi/items/4f88763e620a8d329163)
* [第5回 Tilemap関連のコンポーネント編](https://qiita.com/RyotaMurohoshi/items/ae46343828634a2d3321)
* [第6回 Tileの描画編](https://qiita.com/RyotaMurohoshi/items/fc473a2dd54441e9b94e)
* [第7回 TilemapとCollider編](https://qiita.com/RyotaMurohoshi/items/8d1e7dfb4a1b2aca0463)
* [第8回 関連リンクとまとめ編](https://qiita.com/RyotaMurohoshi/items/eddbb74e07e79f6c2185)