UnityでRPGを作るpart2 2Dトップビューのマップと衝突判定

# はじめに
前回はこちら
[UnityでRPGを作るpart1 歩行アニメーションと操作 - Qiita](https://qiita.com/_tybt/items/bc9c3be75c04ab547c35)

前回にキャラクターを歩行させるコードを書いたので今回は

- 2Dマップの作成
- 作成したマップのオブジェクトに衝突判定を設定する

をやっていこうと思います。
ちなみに今回の完成物はこちら。だいぶゲームっぽくなります。
そして今回 **記述するコードの少なさに感動しました** (Unity初心者 雑感)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/48727ed4-8052-b97a-5b09-e3c878c053a9.gif" width="600px" />


**一応**
これは勉強の備忘録として作っていますが、やっぱりこういったいかにもなAssetがStoreで提供されていたりします（便利か。はまだ使ってないのでわかりませんが）
[RPG Map Editor - Asset Store](https://assetstore.unity.com/packages/templates/systems/rpg-map-editor-25657?aid=1011lGbg&utm_source=aff)
さすがみんなのUnity。とりあえず探してみるのもアリです :thumbsup: 

# 1. 2Dマップの作製
まずは、マップ用の素材を準備します。
さすがにマップの素材を1から作るのはしんどいので、今回はこちらのサイトの素材を利用させていただきます。 :pray: 
[マップチップ - ぴぽや倉庫](https://pipoya.net/sozai/assets/map-chip_tileset/)

画像をDLしたら前回同様に `Sprite Editor`を利用して、
`32x32`画像なので、 Sliceでいい感じに設定して画像のように分割します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/cfbefdf7-db48-8e8f-18ab-625d5503ee7d.png)

で、前回は キャラクターの画像に対して `Pixel Per Unit = 1`と設定していましたが
今回は、1マス = 1キャラクター に一つのチップをあわせるために`32x32`画像だから
**Pixel Per Unit = 32**を設定します。

ちなみにこの工程は、RPGツクールのようなタイル状にいろんなマス画像が1枚のファイルになったものを分割するための工程なので、一つずつ読み込む場合などは適宜無視してください。

## TileMapとTilePalette
RPGツクール触ったことがある人ならわかるだろう…あのマップ編集機能。
マップチップをイラスト編集のように簡単にできるアレですが
**Unityもそれっぽいのあるよ(?)**といことで、TileMapとTilePaletteです。

名前のとおりですが、タイルの画像を設定して、それをマップ上にタイルのように敷き詰めるための機能です。

Sceneに新しく足していきます。
右クリック -> `2D Object` -> `Tilemap`で新しいTilemapが追加できます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/ca94f2ff-e359-1d09-08ef-67c9ebaa0019.png" width="300px" />
そうすると、`Grid`というオブジェクトが追加されてその中に今回編集対象の`Tilemap`のシーンが追加されています。

次に上部メニュー `Window` -> `2D` -> `Tile Palette`を押すと
いかにもな画面がダイアログで表示されます（画像はパレット追加済みの状態です）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/1ec2f167-a0e7-088b-baaf-ddea108642d6.png" width="300px" />

画像の左上に見えるプルダウンを押すと、`Create New Palette`という項目があるので
押して新しいパレットを作成します。とりあえず設定は下の通り。

|項目|概要|
|---|---|
|Name|わかりやすければ。|
|Grid|Rectangle(default)|
|CellSize|Automatic。作成する大きさが決まってるなら設定するぐらいでOKなはず|

これで、新しいパレットが作成されます。
後々を考えると、「ダンジョン」「城下町」みたいなくくりでしっかり分けておくと管理しやすいかも？

次に先程 Sprite Editorで分割したマップ画像を Paletteにドラッグ&ドロップすると
タイルを保存するために、保存先のフォルダを指定する画面がでるので
**とりあえず専用のフォルダを作成**して`Choose`を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/2509796c-9cf8-7bd8-7aa8-8c2b530b548f.png)
こうすると、分割したマップ画像内のそれぞれのタイルが一つの画像としてフォルダに保存されていきます。

**補足**
パレットにいれたけど、いらない。といった場合は
先程パレット作成時に選んだプルダウンメニューのとなりにある `Edit`という項目を押すことで
編集ができます。消しゴムを選んで、パレットのタイルを選択すれば消えます。
このあとに行うTilemapの編集時は `Edit`状態をもう一度押してOFFにしましょう。

## Tilemapの編集
最初にSceneに用意したTilemapにマップを書いて作成していきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/74e06ff4-aab1-3862-d1c3-5028fe8d9d00.png)

雰囲気でわかると思いますが

- Tile Paletteから書きたいタイルを選択
- Tile Palette上部のペンを選択
- その下にある`Active Tilemap`を選択
    - 複数のTilemapがある場合は切り替える必要があります
    - Scene一覧にある方をクリックして選択しても切り替えられます
- 画面真ん中で思いのままに描いていく

だけで、最初に見せた完成形のようなマップが完成します。
ただ、Tilemapが1枚だけでは 草原の地面の上に木を生やすといったことができないので、複数のTileMapを利用していきます。
後々の衝突判定などでも有用になってくるので、次にレイヤーについて説明します。

## レイヤーの上下関係を設定する
前回のキャラ作成では、複数のオブジェクトを扱わなかったのですが、今回からマップとキャラクターを扱うようになるため、単純に追加していくと下のように**キャラクターがマップの後ろに隠れてしまう**という現象に行き当たると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/d345cb06-eb7f-ee1b-6933-80a81abf2157.png)

そこで、各オブジェクトにレイヤーの位置関係を設定していきます。
現在キャラやマップのオブジェクトには `xxx Renderer`というComponentがセットされていると思います。

- キャラ(Sprite) : Sprite Renderer
- マップ(Tilemap) : Tilemap Renderer

そのComponentに **Sorting Layer**という項目があるので、そこを選択し
**Add Sorting Layer...**という項目をクリックすると下の画像のような画面が表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/ba666f81-327d-5169-29c2-edd17a9a7022.png" width="500px" />

`Sorting Layers`の項目の右下にある+を押して、画像のように

- 歩行可能なタイルを配置するレイヤー
- 移動できない(山や水のような)タイルを配置するレイヤー（後述）
- キャラクターが表示されるレイヤー

を作成してさきほどの画面に戻り、 **Sorting Layer**からそれぞれの作成したレイヤーを設定しましょう。
これで、キャラクターがマップの上に表示される筈です。

**これで、ページ先頭に表示した完成物と同じ見た目になります**

## 歩けるタイルと歩けないタイルを分ける
次の項目として説明していく衝突判定の前段階です。
重ねて表示したいタイルがあったりする場合にレイヤーを分けますが、それだけではなく
特定のレイヤーをまるごと衝突判定の対象とすることで、マップの実装が楽になります。

完成物から歩けるTilemapを除くとこのような状態になっています
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/9198070c-ddaa-1f2c-2904-fb07e392138f.png" width="500px" />

衝突判定の設定などは次項。

# 2. マップに衝突判定を設定していく

半分コード。半分Unityな項目です。
マップを作ると「歩けない道」が当然のように作っていくことになるので
ここでは、その設定と衝突判定に伴うキャラクターの操作を制御する実装をしていきます。

やることは **山や川はすすめずにぶつかるようにする。** です

とりあえず **コリジョン** **Collision** という単語を覚えておきましょう。
検索のときに重要です :innocent: 

## Collider2Dを追加する
キャラやマップのオブジェクトにコンポーネントを足していきます。

- キャラ(Sprite) : `Box Collider 2D`
- マップ(Tilemap) : `Tilemap Collider 2D`

昔ながらのゲーム（というコンセプト)なので衝突判定は厳密にやりません。ので、細かく調整をする必要もありません。
上を追加すると、それぞれ緑色の枠のようなものが表示されます。
以降、この衝突判定領域を示す緑の枠を **Collider**と表記します。

体の上部だけは判定を除いて重なるようにする場合
`Box Collider 2D`の項目にある `Edit Collider`というボタンを押して、Colliderの大きさを調整します。
Box Colliderをセットした場合、Spriteの大きさにあわせてColliderが設定されるので、
それを全長の半分に設定した状態が下です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/6d3c5cef-2f05-c45e-61e8-9734eb267417.png)

マップ(`Tilemap Collider 2D`)の方はデフォルトで、読み込んだ画像の透明じゃない部分がColliderとして設定されていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/0c579f9e-48cc-9cdf-6aba-3957b54e0edc.png)
これはタイルの元になっている画像を選択してinspectorを見ると
**Collider Type**が Spriteになっていると思います。

これを**Grid**に変えると、タイルにあわせた四角の部分がColliderとして設定されます。
この設定を全部変える場合、画像をすべて選択した状態で変更することで一気に切り替えることができます。
改めて編集画面を見ると、タイルにあわせたColliderに変わっていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/5d64af82-fecf-ff50-9ef0-679ea8c99067.png)


## RigitBody2Dの設定をDynamicにする
前回は「昔なつかしいRPGなので」 `body type: kinematic`に設定していましたが
**Body Type : Kinematicは標準の衝突判定が無効になります**

ただDynamicにするだけだと物理演算が有効になってしまい、キャラクターが画面外に落ちていってしまうので**Gravity Scale を 0**にすることで、落ちなくなります
が、これだけでは足りません。

### constraintsの設定で回転を止める
物理演算自体は有効なため、前述の設定だけだとCollider同士のぶつかったときに力が働いた方向にグルングルン回転することがあります。

あまり正確な図ではないですが、イメージ的には下の通り。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/d35f0dc1-6290-0683-3326-7032b28fb9be.png" width="500px" />
とりあえず、なんのコッチャな方もいるかと思いますが、とりあえず書いてあるとおりに実装して完成したら、あとでぜひ試してみてください……カオスですｗ
（下のtweetでは Gravity Scaleが１の状態で見ています）

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">サンプルはドラクエ風でやるからBodyType Kinematicにしてたけど、やっぱりコードいじらないとな -&gt; Dynamicで動かしたらどうなるんだ　って思った結果カオスみのある動きになりました <a href="https://t.co/wnSX4KpWlc">pic.twitter.com/wnSX4KpWlc</a></p>&mdash; とむお (@_tybt) <a href="https://twitter.com/_tybt/status/1144644422375596033?ref_src=twsrc%5Etfw">2019年6月28日</a></blockquote>

そういった回転を抑えるためにコードで `Regitbody2D`に対して
下記のような設定を行います。

```
this.rigidBody.constraints = RigidbodyConstraints2D.FreezeRotation;
```
`constraints` まさに制約ですね
これで、上記のようなクルクルは発生しなくなります。

**2019/09/23 追記**
コメントご指摘より
UI上でもFreeze Rotationの設定ができるみたいです
設定項目は Rigidbody2Dの下の項目 (確認済み)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/2ada305c-fde7-e260-89f1-36a55d601a02.png)

ご指摘ありがとうございました :pray:
**2019/09/23 追記 : ここまで**
 

ソレ以外の設定はこちらをご覧ください。
[RigidbodyConstraints - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/current/ScriptReference/RigidbodyConstraints.html)

## コードを書く
毎度最後のコードのターンです。今回はゆるっと説明します。
前回からの説明なので、キャラクターにこのクラスはbind済みであるとします。

ここでは前回作った `MoveController`を修正します
[UnityでRPGを作るpart1 歩行アニメーションと操作 - Qiita](https://qiita.com/_tybt/items/bc9c3be75c04ab547c35#%E3%82%AD%E3%83%BC%E6%93%8D%E4%BD%9C%E3%81%A7%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88)

```C#:MoveController.cs
public class MoveController : MonoBehaviour {
    public readonly float SPEED = 0.1f;
    private Rigidbody2D rigidBody;
    private Vector2 input;

    void Start() {
        this.rigidBody = GetComponent<Rigidbody2D>();
        // 衝突時にobjectを回転させない設定
        this.rigidBody.constraints = RigidbodyConstraints2D.FreezeRotation;
    }

    private void Update() {
        // 入力を取得
        input = new Vector2(
            Input.GetAxis("Horizontal"),
            Input.GetAxis("Vertical"));
    }

    private void FixedUpdate() {
        if (input == Vector2.zero) {
            return;
        }
        // 既存のポジションに対して、移動量(vector)を加算する
        rigidBody.position += input * SPEED;
    }
}
```

**Unity先輩強すぎませんか？**

…すいませんちょっと感動しました。なぜかというと
**「衝突判定されたら、座標を戻す」**的な処理を一切かかなくていいみたいです。
上のコードだけで次のように「山や川にぶつかったら移動させない」みたいな動きができます。

# 完成物確認

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39229/48727ed4-8052-b97a-5b09-e3c878c053a9.gif" width="400px" />

ほぼUnityのUI上の設定だけでできました。

# まとめ
Unityってすごいですね（ボキャ貧）

最初は「衝突とみなされたら、コード上で位置を戻す」とかやらなきゃいけないのかと思っていましたが、ソレが不要というのはありがたい。
ただ、この衝突判定のために 2Dトップビューでは不要な物理演算(Dynamic)を有効にして、Gravityを0にしたり回転を無効にしたりやっているので、あとで辛い目をみるかは…今考えても仕方ないので今後発生したら書きます :innocent: 

次は

- 人や宝箱に対してボタンを押したらイベントの実行
- アイテム情報の管理

的なものでもやっていこうかなと思います。