0.完成像
見にくいかもしれないですが、こんな感じで障害物をよけてプレイヤー(このGIFの勇者のスプライト)を追跡する敵(このGIFのおばけのスプライト)を作りたいと思います。もう少し詳しく言うと、2DのUnityゲームでTilemap(タイルマップ)をもとにNavMeshを実装する、というものです。
1.【注意点】NavMeshについて
この記事を見ている皆さんはきっと、「NavMeshって2Dでは実装できないのかなー」という感じの人が大半じゃないかと思っています。3DでNavMesh自体は触ったことがあって、それありきで2Dでも、という人ですね。
もしご存じない方は、一度3DでNavMeshを軽く触ってから、この記事を読まれることをおすすめします。なぜなら、NavMeshの2Dバージョンは正規のUnityエディタだけでは実装できないからです。そんな応用編をやる前に、まずは普通に3DでNavMeshを扱って前提知識をインプットしておきましょう。
2.NavMesh2Dの拡張
まずはここにアクセス。
Unityテクノロジー公式から、2DバージョンのNavMeshが作成されているので、これをダウンロードします。
ZIP形式でダウンロードしたら、即解凍。
解凍後のフォルダを、NavMeshComponents-2019.3-2D→NavMeshComponents-2019.3-2D→Assets→…
と辿り、下の画像のような画面に到達したら、指定のフォルダをUnityのプロジェクトのAssets下にインポートします。
3.Tilemapの作成
2DのNavMeshはTilemapを前提にするものなので、まずはTilemapを作成しましょう。
TilemapはUnityの2Dの基本かと思いますので、この記事ではそれが完成した前提で進めていきます
設定面で必要な工程としては、Tile(タイル)の設定になります。
NavMeshの障害物となるTileに関しては、Collider TypeをNoneに変更します。
Tileの一部だけ障害物にしたい場合は、Sprite EditorのCustom Physics Shapeで調整できます。
気になる人はググってみてください。
4.NavMeshの形成
ではでは、本題のNavMeshを形成していきましょう。
1.Unityエディタ左上メニューからWindows→AI→Navigation
と進んでNavigationウィンドウを出す。NavigationウィンドウをAgent→Radius
と進み、Radiusの項目を小さい値に設定する。(所感としては0.05あたりがいいのでは、と思っている。個人差あり)
2.ヒエラルキー上の Tilemap
を選択、Tilemap Colider 2D
と Nav Mesh Source Tag 2D
のコンポーネントを追加する
3.適当に空オブジェクトでも作って、そこにNav Mesh Builder 2D
のコンポーネントを追加する。追加後、Bakeのボタンを押す。
4.NavigationウィンドウとSceneビューを並べてみよう。Sceneビューで、NavMeshが青く形成されているのがわかるはず…!
5.しっかりBakeできているな、と思ったら、最後にNav Mesh Builder 2D
のコンポーネントのBake On Enable
の項目にチェックをいれよう。ここを忘れがち!
5.NavMeshAgent2D
さて、NavMeshまで形成できたらあとはNavMeshAgentをつけて…!と、いきたいところですが、なんとNavMeshAgentは2Dに対応していません。
なので、2D版のNavMeshAgentを簡単に作っておきました。
こちらのスクリプトになります。
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を利用した挙動を実装する
サンプルのスクリプトとしたら、こんな感じでしょうか。
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
は主に追跡しかできませんが、暇なときにほかの機能も追加していこうと思います。
ここまで読んでいただきありがとうございました。
以上