敵キャラをUnityのナビゲーション機能を使って移動させる

今回はUnityのナビゲーション機能を使って敵キャラクターを移動させてみます。

スクリプトで目的地を設定し移動させる事は可能ですが、大草原のように周りに何もない時はいいですが、木や建物があった場合、壁などにぶつかりながら無理やり移動せざるを得ない、もしくは壁に突き進むという状態になりました。

そこでナビゲーションの機能を使って敵キャラクターをスムースに移動出来るようにします。
移動出来る場所をNavMeshして作成し、移動するキャラクターにNavMeshAgentというコンポーネントを追加します。

目的地を設定すれば、壁にぶつかりながら移動せず、最適な移動をしてくれます。

スポンサーリンク

NavMeshを作成しキャラクターが移動出来る場所を作成

まずはNavMeshを作成し、移動できる場所を作成します。Terrainで作ったフィールドを選択しインスペクタでStaticにチェックを入れます。

Terrainに関しては

UnityのツールTerrainを使ってフィールドを作成する
UnityのツールであるTerrainを使ってゲームに使用するフィールドを作成していきます。

を参照してください。

ナビゲーション1

WindowメニューでNavigationを選択します。


ナビゲーション2

NavigationウインドウのBakeでパラメータを設定し、下のBakeを押します。
Bakeのパラメータには移動できる範囲を設定します。

Agent RadiusはNavMeshしたフィールドを移動するエージェントの半径
Agent Heightはエージェントの高さ
Max Slopeは移動出来る角度
Step Heightは超えられる段差

ここで設定するAgent RadiusとAgent Heightは後ほどキャラクターに設定するNavMeshAgentコンポーネントの
サイズと合わせた方がいいかもしれません。

Generated Off Mesh Linksの

Drop Heightはこの値以下であれば飛び降りる為のオフメッシュリンクを作成します。
Jump Distanceはこの値以下であれば飛び越えるオフメッシュリンクを作成します。

CharacterControllerで設定するパラメータと似たようなものですね。

Bakeを押すとフィールドに青い部分が表示されます。

ナビゲーション3

この青い部分がNavMeshAgentを設定したキャラクターが移動出来る個所になります。
(もちろんキャラクターの移動できる角度や段差が成立しないと高い所には移動できませんが)

これでキャラクターが移動出来る場所が設定出来ました。

NavMeshした場所を移動するキャラクターの作成

次は敵キャラクターにNavMeshAgentコンポーネントを追加します。

ナビゲーション4

上のように敵キャラクターにNavMeshAgentを追加します。

パラメータはSpeedで移動速度、StoppingDistanceで目的地との距離がどのぐらいになったら到着とするかの設定を調整してください。

今まで使っていたCharacterControllerの機能は使わなくなりますので、チェックを外すか削除してもいいのですが、何かしら不具合があった時に元に戻す為残します。

またCharacterControllerの当たり判定はそのまま残し、主人公キャラの攻撃の当たり判定として使用します。

主人公が敵キャラクターを武器で攻撃した時のOnTriggerEnterイベントを発生させるには、
CharacterControllerがついているか、何らかのコライダ+Rigidbodyがついているキャラクターでないとダメっぽいです。

なので、CharacterControllerは残しておきます。

キャラクターを移動させるスクリプトを修正する

敵キャラクターを操作していたMoveEnemyスクリプトの中身を変更します。

CharacterControllerを使ったキャラクターの移動は

Unityでキャラクターの移動をプログラミングしてみる
キャラクターを移動させる為のスクリプトを作成し、キャラクターを動かす機能を作成します

を参照してください。

今まではCharacterControllerのMove機能を使ってキャラクターを動かしていましたが、そこをNavMeshAgentの機能を使って移動するように修正していきます。

やる事はそれほど変わらず、目的地を設定しそこに移動するという機能をそのままNavMeshAgentの機能へと移行させるだけです。

private var agent : NavMeshAgent;

NavMeshAgentを入れておく変数を宣言します。

Start関数内ではNavMeshAgentコンポーネントの取得をします。

agent = GetComponent(NavMeshAgent);
agent.SetDestination(destination);

コンポーネントの取得が終わったらNavMeshAgentのSetDestination関数を使用して目的地を設定します。
destinationの値はCharacterControllerの時でも使用していた目的地が入った変数です。

Walk関数では今までの複雑な処理がいらなくなり、状態がState.chaseだった時に主人公キャラを目的地に設定します。

Agentは目的地が設定されると移動を開始するので複雑な処理が必要なくなります。
またアニメーターのSpeedに設定する値はAgentのspeedを設定します。

Wait関数では待ち時間が終了した時にランダムな目的地を設定するところまでは一緒で、目的地が設定されたら、Agentの目的地に設定するだけです。

NockBack関数内でCharacterControllerのMoveで移動させていた個所をNavMeshAgentの機能で移動させるようにします。

Update関数内で状態によって処理分けしていますが、

agent.remainingDistanceでAgentの目的地との距離が取得出来るのでこれを使い判定しようと思いましたが、Agentに設定している敵キャラの子要素にSearchAreaを持っておりこの範囲もAgentと目的地の距離の計算に使用される為、Vector3.Distanceで敵キャラと目的地との差で計算するようにしました。

agent.remainingDistanceを使った場合、主人公キャラがSearchAreaに入った途端に敵キャラが攻撃を開始してしまいます。

敵キャラと目的地との距離が0.3以下になったら到着処理をします。
到着したらその場で待ち状態にしています。

また主人公キャラを追いかける状態で距離が近づいたら攻撃する処理も同じです。
攻撃している間は敵キャラを動かしたくないので、Attack関数内でarrivedをtrueにし、到着した場合は現在の位置を目的地に設定します。

念のためAttack関数内でagent.Stop()関数を呼び出しエージェントをストップしています。

それでは敵キャラの修正が終わったのでUnityの実行ボタンを押して試してみましょう。

ナビゲーション5

今までちゃんとしていたのに、敵キャラが急に主人公キャラにめり込むようになりました・・・(+_+)
この原因はNavMeshAgentにしたせいです。

NavMeshにした場所はBakeした時に侵入可能場所と不可能な場所でわけられましたが、主人公キャラは動的に移動しますので、Bakeは出来ません?

そこで主人公キャラクターにめり込まないようコンポーネントを設定する必要があります。

NavMeshAgentが障害物として認識するようになる機能を追加する

Agentに設定したキャラクターが主人公キャラクターを障害物として認識する必要があります。
Agentが障害物として認識する為にはNavMeshObstacleコンポーネントを取りつける必要があります。これを主人公キャラに設定します。

ナビゲーション6

Carveの設定をチェックするとスムースに避けるようになります。
が、NavMeshObstacleの範囲が広い場合、敵が攻撃する時にスムースに避けて主人公に当たらなくなります。

ここらへんはObstacleの範囲、敵のAgentの到着の距離、攻撃を開始する時の主人公キャラとの距離の調整が必要です。

これでObstacleの設定が終わったので、実行してみましょう。

ナビゲーション7

上のように敵キャラがちゃんと主人公キャラを避けるようになりました。

ナビゲーション機能を使ってみての感想

これでナビゲーションを使って敵キャラクターを移動させる事が出来るようになりました。

注意点としては、NavMeshで指定するオブジェクトをStaticにしてBakeをする事です。
建物等がStaticされていないとすり抜けていってしまいます。
動かない障害物を作成する場合は必ずStaticにしてBakeします。

また1つのシーン上で二つのステージを置いていて試しに切り替えてBake等していると混乱します。

Stage1とStage2のように二つのステージがありStage1だけStaticにしてBake、その後Stage2を使うようにした場合、
同じシーンにあるのでBakeされたStage1のNavMeshをStage2で使う事になります。

二つのステージがある場合はすべてをStaticにしBakeしておく方がよさそうです。
わたしの場合、本ゲーム用とここの記事用のTerrainを使っていて、急に不具合に直面し、原因不明でしたが、これが原因でした。

そもそも1つのシーンのヒエラルキー上に二つのTerrainを使う事はなかなかなさそうですが・・・・。
(そんな事もない?)

BakeされたNavMeshファイルはそのシーンと同じ階層にシーン名のフォルダが作成されその中に入るようです。

いやぁ、ナビゲーション機能は本当に便利ですね!
設定すれば自然なルートで移動してくれますので、敵が巡回しているようなAIが簡単に作成出来ます。

ナビゲーション機能では他にも使いたい機能があるので、そちらも使えるようになったら記事にしたいと思います。

Unityのナビゲーション機能を使って決まった場所を巡回させる
Unityのナビゲーション機能を使用して敵キャラクターを巡回させる機能を作成していきます

もナビゲーション機能を扱った記事になります。