Unity2018.3のIsometric Tilemapを要約してみた

#Isometric Tilemapとは
IsometricTilemapとは、等角図法で描かれたタイルマップのことです。Unity2018.3から標準搭載された機能の1つです。図1のような描画のゲームを作成することが可能になります。

![2D_IsoTilemap_14.png](https://qiita-image-store.s3.amazonaws.com/0/248882/9cd24395-7050-052d-e3d9-a04434b4b0cb.png)
図1

ここでは、[UnityのドキュメントにあるIsometric Tilemaps](https://docs.unity3d.com/Manual/Tilemap-Isometric.html)を中心に要約していこうと思います。ドキュメントの和訳ではないため、独自の見解が入っていることをあらかじめお伝えしておきます。また、今回は[Kenny Vleugles様のIsometric Landscape](https://www.kenney.nl/assets/isometric-landscape)を使用させていただいております。
#Import and preparing Sprites
Import Settingsを図2のようにして、Assetsフォルダ配下に画像をインポートしてください。

![2D_IsoTilemap_0.png](https://qiita-image-store.s3.amazonaws.com/0/248882/53ea9dd7-ed13-83cf-6696-11580ec632fe.png)
図2

Isometric TilemapとしてSpriteをインポートする際、以下の設定にすることを推奨します。詳しくは[Unity公式ドキュメント Texture Type:Sprite(2D and UI)](https://docs.unity3d.com/Manual/TextureTypes.html#Sprite)を参照してください。

1. **Texture Type** - **Sprite(2D and UI)**にしてください。
2. **Sprite Mode** - 1つのテクスチャに1つのスプライトしかない場合には、**Single**を選択してください。分割して使用する場合には**Multiple**にしてください。
3. **Pixels Per Unit(PPU)** - この値は「何ピクセルが何Unit(Unity上の単位)に相当するか」を決定します。つまりタイルマップとしてスプライトがどの程度の大きさで描画されるかを決定します。Gridコンポーネントの**Cell Size**も同様で、これは1つのセルの大きさが、何Unitかを決定することができます。

例として、下の図3の元画像は256×128の画像です。Cell Sizeは(1, 0.5, 1)となっています。図3の右画像ではPPUが256で設定したため、1Unit=256pixelとなり、またCell SizeのXは1のため、1つのセルに1つのスプライトが描画されています。

![2D_IsoTilemap_1.png](https://qiita-image-store.s3.amazonaws.com/0/248882/46169db0-aef2-176e-017c-ce6b671a9c97.png)
図3：右がPPU256、左がPPU128

図4、図5のようにPPUを変化させてスプライトのサイズを変化させても、タイルマップ上で置かれる位置は同じ位置です。（マウスカーソルのある白いセルを中心にスプライトが置かれる）

![a918fdd49eb8d3c19f769f3ead94c130.png](https://qiita-image-store.s3.amazonaws.com/0/248882/84a768a2-2f03-5c17-2b23-28035d7bbc0c.png)
図4：元画像のX=133pixel、PPU=100
![a0c4c09cd43940dc15a8213d35dbe04b.png](https://qiita-image-store.s3.amazonaws.com/0/248882/f6fcb6c7-76cf-7202-ef3d-0a7ce44030d2.png)
図5：元画像のX=133pixel、PPU=40

1. **Mesh Type** - **Tight**に設定すると、透明部分をくりぬいて描画します。**Full Rect**にすると、isometricでは不必要な、四隅の透明部分を描画してしまうためおすすめされません。
2. **Generate Physics Shape** - Physics2Dとインタラクトする必要のない場合、この部分はチェックしないで大丈夫です。チェックを入れると、Tilemap Colliderに必要なタイルマップの物理形状を生成します。（ただ、私の検証した場合では、チェックを入れずにTilemap Colliderをオンにした場合、Colliderが生成されました。特定のSpriteのColliderを切るには、タイルアセットのColliderTypeをNoneにすると可能でした。）タイルのセルサイズに合ったColliderを生成したい場合は、タイルアセットのColliderTypeを**Grid**にしてください。

スプライトをインポートし、タイルマップにセットした後、セルのアウトラインに合わせてSprite Editorを微調整してください。スプライトの**Pivot**も適宜調整してください。（私的にはCenterが枠内にぴったり収まると思います）
**Sprite Mode** を**Multiple**にしてインポートした場合、それぞれのスプライトに対して設定を行ってください。

#Creating an Isometric Tilemap
Isometric Tilemapを作成する際、[通常のタイルマップ作成](https://docs.unity3d.com/Manual/Tilemap-CreatingTilemaps.html)に加えて追加のステップが必要になります。**GameObject>2D Object>Isometric Tilemap>Isometric Z as Y Tilemap**を進んでください。
Isometric Tilemapを作成したのち、適切に表示するための追加の設定として、ProjectとGridの設定をする必要があります。

###Custom Axis Sorting
Isometric Tilemapを適切に描画するためには、奥にあるタイルが手前にあるタイルより先に描画し、疑似深度を作成する必要があります。この設定をシーン全体に適用するためには、**Edit>Project Settings>Graphics>Camera Settings>Transparency Sort Axis*のXYZを(0, 1, 0)に変更します。（図6）

![Iso-Tilemap-AxisSorting.png](https://qiita-image-store.s3.amazonaws.com/0/248882/bf654eff-611d-f2ec-2bb7-14f5de469dc0.png)
図6

この設定にすると、Yが高い値の部分からレンダリングされるため、奥の物体が手前の物体よりも先に描画されます。
高さを使用したい**Isometric Z as Y Tilemap**を使用する際には、Transparency Sort Axis=(0, 1, 0)だと高さがレンダリング順に考慮されない場合があります。（図7）

![Iso-axis-compare.png](https://qiita-image-store.s3.amazonaws.com/0/248882/c0985478-5908-4787-abbc-2621e942abe0.png)
図7：左が(0, 1, 0)の場合、右が(0, 1, -0.26)の場合。右はきれいに積み重なって見える

Transparency Sort Axisを**(0, 1, -0.26)**にすると、高さ情報を元に適切にレンダリングされます。

高さのある建物を作成する場合、タイルマップ上の同じ位置に、z値を変えてタイルを置きます。図8→図9のように、別のタイルの場所にスプライトをおいていくのではなく、図8→図10のように、同じ位置にz値を変えて置いていきます。

![1f66a5dea51e1431871c7b60f4a8a208.png](https://qiita-image-store.s3.amazonaws.com/0/248882/51e5c4ab-7049-e0b3-927a-a00ffd8ab2ee.png)
図8：白い枠線がタイルを置いた場所
![4818e6e4b743f673b044b4c84d666e62.png](https://qiita-image-store.s3.amazonaws.com/0/248882/a4d0022a-69ca-42bd-4fd9-b7a2f88e0c7e.png)
図9
![699489426e6f89a61520e0b27ff353da.png](https://qiita-image-store.s3.amazonaws.com/0/248882/204807e9-09fb-fa3c-3358-fe02f8ca6d5a.png)
図10：青い枠線は、高さ情報により実際に描画される位置
###Grid Cell Size settings
![2D_IsoTilemap_Gridupdate.png](https://qiita-image-store.s3.amazonaws.com/0/248882/6fc9f5e7-e741-108e-c038-a8203c88dc05.png)
図11：デフォルトのレイアウト設定

|||
|:-----------:|:-----------:|
|Isometric|規定値のIsometric Tilemapを作成する|
|Isometric Z As Y|Z値を疑似Y値として追加することができるIsometric Tilemapを作成する。これにより、Y軸へのオフセットが生まれ、高さの錯覚が生じます。|

Isometric Tilemapは二等角投影法や等角投影法を使用します。詳細は[こちらの記事](https://en.wikipedia.org/wiki/Isometric_computer_graphics)で確認してください。
GridのCell Sizeを変えることで、セルの角度を変化させることができます。XYZ=(1, 0.5, 1)では鋭角60°、鈍角120°のひし形となります。正確な等角投影法（isometric projection）にする場合は、Yを**0.57735**にしてください。これは横$\sqrt3$, 縦1の比率のため、Xを1とした場合Yは$1/\sqrt3=0.57735.....$になるからです。

#Creating a Tile Palette for an Isometric Tilemap
1. **Window>2D>Tile Palette**からタイルパレットウィンドウを開きます。
2. 図12のように**Create New Palette**を選択します。

![2D_IsoTilemap_4.png](https://qiita-image-store.s3.amazonaws.com/0/248882/6c5a95cc-0c4c-eac4-3685-ff63d6c87f91.png)
図12

3. Grid typeでIsometric/Isometric Z As Yを選択します。
4. Cell SizeをManualにします。図13のように、XとZはデフォルトのままの値にしておいてください。YはGridコンポーネントのY値と同じにします。

![2D_IsoTilemap_6.png](https://qiita-image-store.s3.amazonaws.com/0/248882/d0eafc48-0bc2-091d-6853-6febc86ea393.png)
図13

5. Createボタンを押すと、新たな**Tile Palette Asset**が作成されます。（図14）

![2D_IsoTilemap_5.png](https://qiita-image-store.s3.amazonaws.com/0/248882/8ae4dbc7-d854-3849-2975-21cc83d45a66.png)
図14

6. タイルパレットの内容を変更したいときは、Asset配下にある変更したいタイルパレットPrefabに変更を加えてください。

###Adjusting the Tile height in the Palette
Isometric Z as Y タイルマップでタイルを描いている際、**Z Position**にて高さを変更することが可能です。（図15）Z値はY軸のオフセットに変換され、異なったZ値で塗られたタイルは、異なった高さを持つように見えます。Z Positionはタイルマップパレットの下部から変更できます。

![2D_IsoTilemap_7.png](https://qiita-image-store.s3.amazonaws.com/0/248882/df440f46-9129-33cc-83ae-14b37c89361b.png)
図15

任意の整数を入力してZ値を調整してください。Z値をリセットまたは変更するまでは、タイルはその高さで塗られ続けます。元に戻すにはResetボタンを押すか、Z値を0にしてください。
シーンビューでは、Z値0での位置が白色のアウトラインで表示され、入力したZ値での位置が青色のアウトラインで表示されます。（図16）

![2D_IsoTilemap_8.png](https://qiita-image-store.s3.amazonaws.com/0/248882/df081284-82a4-9a51-d678-50ebd2c26e3b.png)
図16

タイルブラシが選択されている場合、Z値を変更するための以下のショートカットキーが使用可能です。

|||
|:-----------:|:-----------:|
|-|Z値を1増やす|
|+|Z値を1減らす|

タイルを消去するには、**Erase Brush**をターゲットと同じZ値で使用してください。Erase Brushは異なった高さのタイルを消去できません。

#Tilemap Renderer Modes
TilemapRendererコンポーネントは、シーン上においてタイルマップをレンダリングします。Unityではタイルマップ作成時に、デフォルトでアタッチされます。TilemapRendererはひとまとめにして描画する**Chunk Mode**と、個々に描画する**Indivisual Mode**が存在します。Render Modeはタイルマップのスプライトを、どのようにソートして描画するかに影響します。

###Rendering in batches with Chunk Mode
デフォルトではChunk Modeに設定されています。Chunck Modeでは、タイルマップ上のスプライトを纏め、一括で描画します。そのためSet Pass Callを抑えることが可能ですが、複数のタイルマップが重なった際に、一貫して同じ描画がされないことがあります。(図17）

![2D_IsoTilemap_10.png](https://qiita-image-store.s3.amazonaws.com/0/248882/9963ff59-02d9-df63-ae9d-a51b976c5890.png)
図17：このようにタイルマップの枠内からはみ出した部分の描画が安定しない

この問題を解決するためには、1つの**Sprite Atlas**としてまとめると解決できます。Sprite Atlasの使い方は[LIGHT11様の記事](http://light11.hatenadiary.com/entry/2018/06/04/230848)を参考にさせていただきました。Sprite Atlasにまとめたのち、Playモードにすると正しく描画されます。これはPlayモード時のみ正しく描画されます。（図18）

![2D_IsoTilemap_12.png](https://qiita-image-store.s3.amazonaws.com/0/248882/0996790f-d7ae-7e5c-4038-718f76feecfd.png)
図18：Sprite Atlasに纏め、描画したもの

###Rendering individually with Individual Mode
Individual Modeではシーン上でのレンダリング結果を考慮して、個々のスプライトを描画します。（図19,20）他のRendererが、タイルマップ上のオブジェクトやスプライトに対してインタラクトする際に使用してください。
このモードではTilemap Rendererがそれぞれのタイルマップの位置に基づいて、正しい描画順にソートします。

![2D_IsoTilemap_14.png](https://qiita-image-store.s3.amazonaws.com/0/248882/3f8a9949-4d25-a043-26d3-6a0232a628f6.png)
図19：Indivisual Modeだと塔のスプライトの裏側にキャラクターが描画され、また地面の上にキャラクターが描画されている

![2D_IsoTilemap_15.png](https://qiita-image-store.s3.amazonaws.com/0/248882/4d275517-5108-4518-3871-37fa63e9b611.png)
図20：Chunck Modeだとキャラクターは地面の裏にいってしまう

>
基本的にSpriteAtlasにまとめたタイル群は、ModeをIndivisualにしてもChunkにしてもSetPassCallはあまり変わりません。ただ、Indivisualにすることでタイルの上下の重なりがおかしいといった挙動がなくなります。
また、図21のようにキャラクターの一部が埋まるといったことがあります。その場合は、キャラクターのSprite RendererのSprite Sort PointをPivotにし、キャラクターを埋めている側のタイルのPivotをCustomにして調整してください。（図22)ただ、すこし強引なやり方なので、より良い解決法があるのではと思います。

![425af2d15389d976acaabc9710c6c7fc.png](https://qiita-image-store.s3.amazonaws.com/0/248882/7906446c-7b45-31d0-ad3d-2d50c1485c66.png)
図21：樽がキャラクターの代わりです。このとき地面のPivotはCenterです。
![4cb0b87126bf20258060273697a706e3.png](https://qiita-image-store.s3.amazonaws.com/0/248882/4f5782d5-c9bf-1cbe-9b55-81eda4619856.png)
図22：今回は地面のPivotをX1, Y2にした時にうまくいきました。

#Scriptable Brushes
[GridBrushBase](https://docs.unity3d.com/ScriptReference/GridBrushBase.html)クラスはタイルパレット上の操作を継承し使用することが可能になるものです。独自の挙動を持つブラシを作成することも可能です。例えば[public virtual void ChangeZPosition(int chnage)](https://docs.unity3d.com/ScriptReference/GridBrushBase.ChangeZPosition.html)ではブラシのZ値を変更することができます。[public virtual void ResetZPosition()](https://docs.unity3d.com/ScriptReference/GridBrushBase.ResetZPosition.html)ではZ値をリセットすることができます。

#終わりに
本記事は[Unity公式リファレンス Version2018.3](https://docs.unity3d.com/Manual/Tilemap-Isometric.html)のIsometic Tilemapsを元にして要約されています。また、Isometic Tilemapの議論が[Unity Forum](https://forum.unity.com/threads/isometric-tilemap-sorting-issues.554914/)にて活発に行われているので、何か分からなかった場合はそちらに解決法が掲載されている可能性があります。
バージョンの変更等に際し、記事通りにいかない場合もありますのでご了承ください。