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

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

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

そこでナビゲーションの機能を使って敵キャラクターをスムーズに移動出来るようにします。

移動出来る場所をNavMeshして作成し、移動するキャラクターにNavMeshAgentというコンポーネントを追加します。

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

↑のような感じです。

CharacterControllerを使った移動とほとんど変わらないように見えますが、途中に壁等の障害物がある時はそれを回避し進めるようになります。

スポンサーリンク

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

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

Terrainに関しては

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

を参照してください。

ナビゲーション1

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

ナビゲーション2

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

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

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

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

Generated Off Mesh Linksの

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

オフメッシュリンクはナビゲーションエリアが切れている部分を移動する為のリンク地点です。

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

ナビゲーション3

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

青い部分はNavigation AreaでWalkableが設定されているエリアで、NavigationウインドウのObjectタブでゲームオブジェクトのNavigation Areaを変更する事が出来、そこで、別のエリアを指定してからBakeすると違う色が表示されます。

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

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

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

ナビゲーション4

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

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

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

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

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

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

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

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

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

Unityで3Dキャラクターモデルを配置し、キャラクターをCharacterControllerの機能を使って移動させるようなプログラミングをしてみます。

を参照してください。

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

フィールドとStartメソッド

JavaScriptでスクリプトを組んでいる場合は問題ないですが、C#で記述している場合にUnityが5.5バージョン?以降のNavMesh系のクラスはUnityEngine.AIパッケージ以下にあるので、

using UnityEngine.AI;

というusingディレクティブを入れておくとスクリプト中で

UnityEngine.AI.NavMeshAgent → NavMeshAgent

という簡単な記述が出来ます。

それ以前のバージョンであればusingディレクティブを追加する必要はありません。

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

NavMeshAgentを入れておくフィールドを宣言します。

攻撃時に主人公キャラクターの方向を徐々に向かせる為、回転スピードのフィールドを宣言します。

Startメソッド内ではNavMeshAgentコンポーネントの取得をします。

敵キャラ移動処理

次は移動処理部分の変更です。

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

でナビゲーションエージェントの目的地を設定する事が出来ます。

Agentは目的地が設定されると移動を開始するので複雑な処理が必要なくなります。

アニメーションパラメータのSpeedにはagent.disiredVelocity.magnitudeを渡す事にします。

エージェントの現在の速度はagent.velocityでも得られますが今回はagent.disiredVelocityで回避行動による潜在的な速度を考慮したものを使う事にします。

Speedにはfloat値を渡す必要があるのでmagnitudeを使って速度を速さに変換して渡しています。

敵のアニメーターコントローラーでIdle→Walk、Walk→Idleの遷移条件でSpeedを1としていたので0.1に変更しておきます。

敵が目的地に到着したり、主人公キャラを攻撃する距離はagent.remainingDistanceを使用するよう変更します。

agent.remainingDistanceでエージェントの目的地との距離を取得出来ます。

キャラクターが攻撃状態になったら主人公の方向を徐々に向かせるようにします。

まずは相手の位置から敵キャラの位置を引いて主人公キャラクターの方向を取得します。

ここで相手方の位置のY軸だけ自身(敵)のY軸の位置にします。

こうすることでY軸以外の回転を行わないように出来ます。

プレイヤーの方向を取得出来たらVector3.RotateTowardsを使って敵の方向をプレイヤーの方向へと徐々に向かせた方向dirを取得します。

最後にQuaternion.LookRotationを使って方向の角度を計算し、それを敵の角度に代入しています。

敵の攻撃時に主人公がその場所から逃げようとするとその方向に向きながら攻撃するのでより敵の攻撃が当たりやすくなります。

敵キャラ状態変更メソッドSetStateの修正

次に状態変更メソッドSetStateを変更します。

それぞれの状態へと変更した時にエージェントの移動を止める必要がある場合は

を使用してエージェントを止めます。

MyState.WalkやMyState.Chaseの場合は移動を再開させる為に、

を使って移動の再開をしています。

Unity5.6以降のバージョンではStopとResumeではなく

↑のようにisStoppedプロパティをオン・オフする事でエージェントの停止・再開をします。

Stop(停止)に対応する場合はisStoppedにtrueを入れ、Resume(再開)に対応する場合はisStoppedにfalseを入れます。

それでは敵キャラの修正が終わったので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のナビゲーション機能を使って決まった場所を巡回させる機能を作成していきます。

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

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. 名無し より:

    完成したスクリプトを見てみたいです