2DでNavMeshを実装する方法-初心者に優しく-

# 0.完成像
見にくいかもしれないですが、こんな感じで障害物をよけてプレイヤー（このGIFの勇者のスプライト）を追跡する敵（このGIFのおばけのスプライト）を作りたいと思います。もう少し詳しく言うと、2DのUnityゲームでTilemap（タイルマップ）をもとにNavMeshを実装する、というものです。
![test2d-test2-PC_-Mac-_-Linux-Standalone-Unity-2020.3.21f1-Personal_-_DX11_-2022-02-11-20-43-15_Trim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/10f48f9b-2dff-b90a-bd48-13369a1cc0bb.gif)

# 1.【注意点】NavMeshについて
この記事を見ている皆さんはきっと、「NavMeshって2Dでは実装できないのかなー」という感じの人が大半じゃないかと思っています。3DでNavMesh自体は触ったことがあって、それありきで2Dでも、という人ですね。

もしご存じない方は、一度3DでNavMeshを軽く触ってから、この記事を読まれることをおすすめします。なぜなら、NavMeshの2Dバージョンは正規のUnityエディタだけでは実装できないからです。そんな応用編をやる前に、まずは普通に3DでNavMeshを扱って前提知識をインプットしておきましょう。

# 2.NavMesh2Dの拡張
まずはここにアクセス。

https://github.com/Unity-Technologies/NavMeshComponents/tree/2019.3-2D

Unityテクノロジー公式から、2DバージョンのNavMeshが作成されているので、これをダウンロードします。

![qiita1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/af6e53db-730f-cd18-a995-701e49aec843.png)

ZIP形式でダウンロードしたら、即解凍。
解凍後のフォルダを、```NavMeshComponents-2019.3-2D→NavMeshComponents-2019.3-2D→Assets→…```と辿り、下の画像のような画面に到達したら、指定のフォルダをUnityのプロジェクトのAssets下にインポートします。

![qiita2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/36ef5c64-b772-d067-7815-6945f26a13de.png)

# 3.Tilemapの作成
2DのNavMeshはTilemapを前提にするものなので、まずはTilemapを作成しましょう。
TilemapはUnityの2Dの基本かと思いますので、この記事ではそれが完成した前提で進めていきます

設定面で必要な工程としては、Tile（タイル）の設定になります。
NavMeshの障害物となるTileに関しては、Collider TypeをNoneに変更します。

![qiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/1d611cb0-f828-c34d-4048-cd3c8237c678.png)

Tileの一部だけ障害物にしたい場合は、Sprite EditorのCustom Physics Shapeで調整できます。
気になる人はググってみてください。

# 4.NavMeshの形成
ではでは、本題のNavMeshを形成していきましょう。

1.Unityエディタ左上メニューから```Windows→AI→Navigation```と進んでNavigationウィンドウを出す。Navigationウィンドウを```Agent→Radius```と進み、Radiusの項目を小さい値に設定する。（所感としては0.05あたりがいいのでは、と思っている。個人差あり）

![qiita4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/5aa579ed-8437-314a-e43e-bc704b53b409.png)

2.ヒエラルキー上の ```Tilemap``` を選択、```Tilemap Colider 2D``` と ```Nav Mesh Source Tag 2D``` のコンポーネントを追加する

![qiita5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/66c5d5fe-c072-05eb-6110-4dc80a7a28b7.png)

3.適当に空オブジェクトでも作って、そこに```Nav Mesh Builder 2D```のコンポーネントを追加する。追加後、Bakeのボタンを押す。

![qiita6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/7f3c61de-10c5-cf00-5490-52c0e3d9ed05.png)

4.NavigationウィンドウとSceneビューを並べてみよう。Sceneビューで、NavMeshが青く形成されているのがわかるはず…！

![qiita7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/7e4d41ff-a722-fae3-5a4e-091cbeb20930.png)

5.しっかりBakeできているな、と思ったら、最後に```Nav Mesh Builder 2D```のコンポーネントの```Bake On Enable```の項目にチェックをいれよう。ここを忘れがち！

![qiita8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1233990/5f9ef501-d267-b746-23d9-f2a82fca339d.png)

# 5.NavMeshAgent2D
さて、NavMeshまで形成できたらあとはNavMeshAgentをつけて…！と、いきたいところですが、なんとNavMeshAgentは2Dに対応していません。

なので、2D版のNavMeshAgentを簡単に作っておきました。
こちらのスクリプトになります。

```c#:NavMeshAgent2D.cs
using UnityEngine;
using UnityEngine.AI;

public class NavMeshAgent2D : MonoBehaviour
{
    [Header("Steering")]
    public float speed = 1.0f;
    public float stoppingDistance = 0;

    [HideInInspector]//常にUnityエディタから非表示
    private Vector2 trace_area=Vector2.zero;
    public Vector2 destination
    {
        get { return trace_area; }
        set
        {
            trace_area = value;
            Trace(transform.position, value);
        }
    }
    public bool SetDestination(Vector2 target)
    {
        destination = target;
        return true;
    }

    private void Trace(Vector2 current,Vector2 target)
    {
        if (Vector2.Distance(current,target) <= stoppingDistance)
        {
            return;
        }

        // NavMesh に応じて経路を求める
        NavMeshPath path = new NavMeshPath();
        NavMesh.CalculatePath(current, target, NavMesh.AllAreas, path);

        Vector2 corner = path.corners[0];

        if (Vector2.Distance(current, corner) <= 0.05f)
        {
            corner = path.corners[1];
        }

        transform.position = Vector2.MoveTowards(current, corner, speed * Time.deltaTime);
    }
}
```

```NavMeshAgent2D``` という名前のスクリプトを作成して、あとは上記のスクリプトをコピペしてください。

# 6.NavMeshAgent2Dの使い方
```NavMeshAgent2D``` は、本物のNavMeshAgentの機能のほんの一部のみを実装したスクリプトですが、使用法としてはNavMeshAgentと同様に使用していただけます。

1.NavMeshを適用したい物体（今回だと敵キャラのおばけ）に```NavMeshAgent2D```をアタッチする
2.敵キャラのスクリプトでNavMeshを利用した挙動を実装する

サンプルのスクリプトとしたら、こんな感じでしょうか。

```c#:FollowPlayer.cs
using UnityEngine;

public class FollowPlayer : MonoBehaviour
{
    NavMeshAgent2D agent; //NavMeshAgent2Dを使用するための変数
    [SerializeField] Transform target; //追跡するターゲット

    void Start()
    {
        agent = GetComponent<NavMeshAgent2D>(); //agentにNavMeshAgent2Dを取得
    }

    void Update()
    {
        agent.destination = target.position; //agentの目的地をtargetの座標にする
		//agent.SetDestination(target.position); //こっちの書き方でもオッケー
    }
}
```

# 7.最後に
最近購入した『Unityプログラミング・バイブル2nd』という書籍によると、「ナビゲーションシステムは、Unityの機能のなかでも枯れた機能となりつつ」あるらしいです。不具合などが少ない反面、近年は大きな機能追加や修正はされていないそうです。
おそらくこの2D版NavMeshも、公式採用されることはないんだろうなぁと思ったり。

現状、上記の```NavMeshAgent2D```は主に追跡しかできませんが、暇なときにほかの機能も追加していこうと思います。
ここまで読んでいただきありがとうございました。

以上