【Unity2D】複数Tilemap編集を手軽にする自作カスタムブラシ

以下の機能を持つ、Tilemap 用のカスタムブラシ`ExBrush`を作成してみました。

1. 移動ツールで、選択範囲内の**全てのコンテンツを同時に動かせる**（複数の Tilemap、`PrefabBrush`で設置したコイン、手動で設置した敵やNPCなど）。![exbrush.gif](https://qiita-image-store.s3.amazonaws.com/0/321683/0e033e63-e46c-868e-736e-9e90d388a16f.gif)
2. ピックしたタイル（タイルグループ）に応じて **Active Tilemap が自動で切り替わる。**![exbrush_switch.gif](https://qiita-image-store.s3.amazonaws.com/0/321683/2a39cced-c167-f31c-e3e4-79da34b3a451.gif)

- プレハブ（プレハブグループ）によって描画先が自動で切り替わる`ExPrefabBrush`も作ってみました。
- タイルグループ、プレハブグループの表現方法として、アセットラベルを使用します。
- 編集対象のコンテンツを識別するために、`ExBrushTarget`という自作のコンポーネントを使用します。
- アンドゥ処理も実装してあります。
- Unity 2017.2.1f1 で動作確認済み。

# 従来手法の課題

Tilemap を主としてレベルデザインを行う環境において、例えば一方通行を実現するための PlatformEffector2D を使おうとした場合、この機能は**タイル単位ではなく Tilemap 単位でしか適用できない**ため、必然的に Tilemap が２つ以上に分かれるという事態になるわけですが、このようなケースで「標準のブラシ」を使ってレベルデザイン作業をする場合、

1. 「一方通行のタイルを描く場合」と「通常のタイルを描く場合」で **Active Tilemap をいちいち切り替える必要がある。**また、切り替えを忘れると作業のやり直しが発生する。
2. 選択範囲を移動したい場合、**全ての Tilemap それぞれを同じ距離だけ移動しなければならない。**
3. 選択範囲のコインや敵キャラを移動したい場合、Tilemap ではないので**標準の移動ツールに切り替えて更に別途動かす必要がある。**

という面倒くささが浮き彫りになります。下のGIFアニメをご覧ください。
![default_brush.gif](https://qiita-image-store.s3.amazonaws.com/0/321683/8e127e5e-c5f6-5da5-4681-88e4a58f364b.gif)
**▲デフォルトのブラシで選択範囲内の物を動かすために必要な工程**

ゲームを実際にデバッグプレイしてみて初めて「ここちょっと狭いな or 広いな」と気づくことはよくあるのですが、2と3のせいでステージの一部分をずらす事すら気軽にできないため「まぁこのままでもいいか」となってしまい、作業効率だけでなくレベルデザインの質までも低下させている節があります。

余談ですがその面倒くささに耐えながら作ったゲームが[こちら](https://store.steampowered.com/app/888990/_Ameagari_no_Hanaby/)です。
次回作を制作開始するにあたり同じ面倒な思いはしたくなかったので、これらの課題を解決するカスタムブラシの制作を決断しました。

# ダウンロード

https://github.com/moyassy/unity-exbrush
以下よりダウンロードしてご自由にお使いください。MITライセンスです。

# 使い方

具体的な手順を書いたほうが分かりやすいと思うので、以下の具体的な構成例で説明します。各自、ご自身のプロジェクトのそれに読み替えてください。

- Hierarchy
    - *Grid*
        - *Basis* : 通常のタイルを設置する Tilemap を持つ。
        - *Platform* : 一方通行のタイルのみを設置する Tilemap を持つ。
        - *Coins* : この下にコインを設置する。
        - *Characters* : この下にキャラクターを設置する。
- Project
    - *block_0.asset* : 普通のブロックのタイルアセット
    - *platform_0.asset* : 一方通行タイルのタイルアセット
    - *Coin.prefab* : コインのプレハブ 

なお、タイルアセットは既にパレットに登録されているものとします。

## 1. 下準備

### 1.1. タイルアセットへのラベル付け

![00153.png](https://qiita-image-store.s3.amazonaws.com/0/321683/09bf6a70-cfb1-ca06-a07d-a24e3d21a43a.png)

*platform_0.asset* を選択し、Inspector 下部の Asset Labels 欄を開いて "Platform" というタイルグループを表すラベルを付けます。この "Platform" というラベル名は GameObject 名の *Platform* とは別物なので、好きな名前でＯＫです。

*block_0.asset* についてはラベル付けを行いません（"Platform" でさえなければ、他のラベルが付いている分には問題ありません）。

### 1.2. コイン・プレハブへのラベル付け

*Coin.prefab* にも "Coin" というラベルを付けます。

### 1.3. `ExBrushTarget` のアタッチ

*Grid* 内の4つの GameObject それぞれに対して`ExBrushTarget`をアタッチし、以下のように設定してください。Labelsには先程設定したラベル名を指定してください（ダブルクォーテーションは不要）。

- *Basis*
    - Type : Tilemap
    - Condition Mode : Reject By Labels
    - **Labels : [ "Platform" ]**
        - "Platform" というラベルが付いていないタイルのみ適合、という意味
- *Platform*
    - Type : Tilemap
    - **Condition Mode : Accept By Labels**
    - **Labels : [ "Platform" ]**
        - "Platform" というラベルが付いているタイルのみ適合、という意味
- *Coins*
    - **Type : For Prefab Brush**
    - **Condition Mode : Accept By Labels**
    - **Labels : [ "Coin" ]**
        - "Coin" というラベルが付いているプレハブのみ適合、という意味
- *Characters*
    - **Type : Manual Objects**
    - Condition Mode : Reject By Labels
    - Labels : [ ]

この設定により、これらの GameObjects が`ExBrush`および`ExPrefabBrush`の編集対象とみなされ、かつ適合するラベルが定義されます。なお、各 GameObject の原点は (0, 0, 0) で揃えてください。

これで、カスタムブラシを使用する準備ができました。

## 2. カスタムブラシ`ExBrush`の使用

`ExBrush` では、単一の Tilemap だけでなく、`ExBrushTarget` が付いている全てのコンテンツを同時に編集することができます。

`ExBrush` を使用するには、Tile Palette ウインドウ下部で Ex Brush を選択してください。

※このカスタムブラシを導入すると、デフォルトのブラシが表示上消えます。デフォルトのブラシが消えて困った場合は、*ExBrush.cs* 内の`CustomGridBrush`属性のパラメータを変更してください。

#### 2.0. 用語について

#####「Tilemap コンテンツ」
- `ExBrushTarget`が Type : Tilemap の場合、同 GameObject にアタッチされている Tilemap を指す
- 今回の例では、*Basis* と *Platform* の Tilemap

#####「GameObject コンテンツ」
- `ExBrushTarget`が Type : For Prefab Brush または Manual Objects の場合、子要素の GameObject の集合を指す
- 今回の例では、*Coins* と *Characters* の子 GameObject の集合

#####「アクティブ」
- ある Tilemap が、Tile Palette の Active Tilemap ドロップダウンボックスで選択されている状態を指す

#### 2.1. 選択、移動ツール

選択範囲内の全ての「Tilemap コンテンツ」と「GameObject コンテンツ」を同時に移動します。
今回の例では、選択範囲内の *Basis* と *Platform* のタイル、*Coins* の子要素、*Characters* の子要素が同時に移動できます。
このツールでは、アクティブになっている「Tilemap コンテンツ」に拘らず同じ結果になります。

#### 2.2. ペンツール

アクティブになっている「Tilemap コンテンツ」に対してタイルを描画します。このとき、ピックしているタイルに適合する「Tilemap コンテンツ」だけがドロップダウンの一覧に表示されるようになります。
今回の例では、*block_0.asset* や空白をピックしている時には *Basis* が、*platform_0.asset* をピックしている時には *Platform* が自動的にアクティブになり、手動で描画先を切り替える必要はありません。

#### 2.3. 矩形ツール

ペンと同じ要領で矩形を描きます。

#### 2.4. スポイト（ピック）ツール

アクティブになっている「Tilemap コンテンツ」に対してスポイトを行いますが、空白だった場合はアクティブ以外も含めた全ての「Tilemap コンテンツ」に対して再度スポイトを行います。全ての「Tilemap コンテンツ」において空白だった場合のみ、空白がピックされます。

#### 2.5. 消しゴムツール

全ての「Tilemap コンテンツ」のタイルを同時に消去します。「GameObject コンテンツ」の内容は消去されません（コインを消しゴムで消去したい場合は `ExPrefabBrush` を使用してください）。
このツールでは、アクティブになっている「Tilemap コンテンツ」に拘らず同じ結果になります。

#### 2.6. バケツツール

ペンと同じ要領でバケツによる流し込みを行います。アクティブになっていない他の「Tilemap コンテンツ」は流し込みで参照されません。

## 3. カスタムブラシ`ExPrefabBrush`の使用

こちらは [2d-extras](https://github.com/Unity-Technologies/2d-extras) で配布されている `PrefabBrush` と大体同じものですが、少し変更を加えています。

- 同じセルを筆が２回通っても、プレハブが二重に描かれないように改良しました。
- 描画先として、Type : For Prefab Brush になっている`ExBrushTarget`のみを候補に含めます。
    - 描画するプレハブアセットのラベルが適合しない場合は描画先の候補から除外されます。
    - 今回の例では、コインの描画時には自動的に *Coins* がアクティブになります。

#### ブラシの作成

`ExPrefabBrush`は`PrefabBrush`と同様、ブラシアセットを作成してから使用します。

- Project ビューで右クリックして Create > Tilemap Brush > Ex Prefab Brush を選びます。
- 作成したものを *Coin Brush* にリネームしてください。
- *Coin Brush* の Inspector で Prefabs に *Coin.prefab* を1つ指定してください。Perlin Scale と Z は変更不要です。

#### ブラシの使用

Tile Palette ウインドウ下部で *Coin Brush* を選択するとコインの編集モードになります。

各ツールごとの機能は以下の通りです。

- 選択、移動、スポイト、バケツ：機能しません（旧`PrefabBrush`と同じ）。
- ペン：コインを描きます。同じ場所には重複して描きません。
- 矩形：コインを矩形に描画します。同じ場所には重複して描きません。
- 消しゴム：コインを削除します。