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

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

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

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

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

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

↑のような感じです。

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

スポンサーリンク

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

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

Terrainに関しては

UnityのツールTerrainを使ってゲームの地形フィールドを作成していきます。

を参照してください。

ナビゲーション1

Navigationウインドウ

UnityメニューからWindow→AI→Navigationを選択します。

Navigationの使い方について少し見ていきます。

Agentタブ

NavigationウインドウのAgentタブを選択するとエージェントの設定をすることが出来ます。

navigationウインドウの画面

デフォルトではHumanoidが用意されており新しくAgent Typesで+を押せばエージェントの設定を追加することが出来ます。

Radiusはエージェントの半径
Heightはエージェントの高さ
Step Heightは飛び越えられる高さ
Slopeは登れる坂の角度

です。

ここで注意が必要なのがデフォルトのHumanoid以外にエージェントタイプを追加したとしてもNavigationウインドウでBakeしたフィールドには適用されないようです(2019/06/21時点)。

Humanoid以外で作成したエージェントタイプが適用されるのは別途インポートする必要があるランタイムにNavMeshベイクが出来るコンポーネントを使った時だけのようです。

Unityのナビゲーション機能でゲーム中にナビメッシュをベイクしてみたいと思います。以前はあらかじめベイクしなければいけませんでしたが、NavMeshComponentsアセットを使うと動的にナビメッシュをベイクする事が出来るようになります。

Areasタブ

Areasタブはナビゲーションのコストを設定することが出来ます。

ナビゲーションウインドウのAreaタブ

コストを上げるとそのエリアを設定したゲームオブジェクトを移動する時はコストがかかる為、別のルートを使った方が最適なルートとなります。

Bakeタブ

BakeタブでNavigation Staticにチェックされたゲームオブジェクトをベイクすることが出来ます。

ナビゲーションウインドウのBakeタブ

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

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

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

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

ここでの設定がNavigation Staticオブジェクトのベイクに反映されAgentタブの設定は先ほど紹介したNavMeshComponentsの設定で使われるのかもしれません。ちょっとややこしいですね・・・(^_^;)

Generated Off Mesh Linksの

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

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

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

ナビゲーション3

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

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

Objectタブ

Objectタブではヒエラルキー上で選択しているゲームオブジェクトのAreaの設定が出来ます。

ナビゲーションウインドウのObjectタブ

Navigation Staticにチェックを入れると選択しているゲームオブジェクトのNavigation Staticにチェックを入れます。

Generate OffmeshLinksにチェックを入れるとベイクした時にオフメッシュリンクを作成します。オフメッシュリンクは連続していない地点を繋ぐリンクのようなものです。

Navigation Areaでは選択しているゲームオブジェクトのエリアを指定します。

これでNavigationウインドウの使い方が分かったので実際にエリアを設定してNavMeshをベイクしてみます。

NavigationウインドウのAreasタブで新たにHard Walkを作成しCostを50にします。

Terrainで作ったフィールドは既にインスペクタでStaticにチェックを入れているので、ヒエラルキー上のTerrainを選択した状態でNavigationウインドウのObjectタブを選択し、AreaにWalkableを指定します。

次にいくつかヒエラルキー上にCubeを配置しインスペクタのStaticにチェックを入れ、選択した状態でObjectタブを押しAreaにHard Walkを指定します。

NavigationウインドウのBakeタブでBakeボタンを押してNavMeshをベイクします。

オブジェクト毎にAreaを変えてナビメッシュベイクしたサンプル

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

Terrainの地面はWalkableエリアなので青色、Cubeの地面はHald Costエリアなので紫色に色分けされています。

上の画像ではCubeを3つ作成し、2つはベイクした時のエージェントの設定でそのまま移動出来る場所に配置し、1つはエージェントが地続きで移動出来ない少し高めに起きました。

高めに置いたCubeが一番左にあるものでオフメッシュリンクが自動で作成されています。

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

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

NavMeshAgentの説明

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

Agent Typeでエージェントのタイプ
Base Offsetでエージェントの位置のオフセット
Speedでエージェントの最高速度
Angular Speedで最高回転速度
Accelerationは最大加速度
Stopping Distanceは目的地とどれだけ近づいたら止めるかの距離
Auto Brakingにチェックを入れると目的地に近づいたら減速させます。
Radiusは障害物を回避する半径
Heightは障害物を回避する高さ
Qualityは回避する品質の高さ
Priorityは指定した数値より低いエージェントの回避を無視します。
Auto Traverse Off MeshLinkにチェックを入れるとオフメッシュリンクのポイントに来たら自動でオフメッシュリンクを移動します。
Auto Repathにチェックを入れると部分的な移動経路の目的地についたら自動で次の経路を探索します。
Area Maskはこのエージェントがどのエリアを移動可能にするかを選択出来ます。

敵には新しくCapsule Colliderを取り付けIs Triggerのチェックを外し物理的に衝突するようにします。

コライダのサイズはCharacterControllerのコライダのサイズと同じにします。

さらにRigidbodyコンポーネントも取り付けIs Kinematicにチェックを入れます。

今まで敵に取り付けていたCharacterControllerの機能は使わなくなりますので削除します。

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

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

敵キャラクターを操作していた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を使って速度を速さに変換して渡しています。

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

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

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

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

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

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

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

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

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

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

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

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

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

移動を再開させる為に、

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

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

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

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

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

敵が主人公をすり抜けるようになった?

今までちゃんとしていたのに、敵キャラが急に主人公キャラにめり込むようになってしまった。という方もいるかもしれません。

ナビゲーション5

この原因は敵の移動機能をCharacterControllerからNavMeshAgentにしたことと敵の衝突判定のコライダをCharacterControllerを使用している場合に起こります。

敵キャラクターの当たり判定に使っていたCharacterControllerコンポーネントを削除し、Capsule Colliderで作成しIs Triggerのチェックを外し、Rigidbodyコンポーネントを取り付けてIs Kinematicにチェックを入れれば主人公に衝突するようになります。

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

Agentに設定したキャラクターが通れる場所はNavMeshがベイクされている所だけです。

ですがNavMeshはあらかじめStaticにした動かないゲームオブジェクトを対象にしているので動くゲームオブジェクトの場合はNavMeshをベイクしても元の位置でしか反映されません。

なのでゲームオブジェクトが動いてもあらかじめベイクされた部分をエージェントが動くことが出来て動くオブジェクトと重なってしまいます(そもそもStaticにしたゲームオブジェクトは動かさない)。

そんな動くゲームオブジェクトをリアルタイムに障害物として認識させる機能がNavMeshObstacleコンポーネントです。

これを動くゲームオブジェクトに取り付けて試してみましょう。

ヒエラルキー上にCubeを作成し、新しくSimpleCubeMoveスクリプトを作成し取り付けます。

これでCubeがX軸をいったりきたりします。

CubeのインスペクタのAdd ComponentからNavigation→Nav Mesh Obstacleを取り付けます。

NavMeshObstacleを取り付ける

Carveの設定をチェックするとスムーズに避けるようになります。

Carve Only Stationaryのチェックを外すとゲームオブジェクトが動いていても有効になります。

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

Navigationウインドウを開いた状態でUnityを実行し、シーンビューでCubeを見てみます。

NavMeshObstacleを取り付けたサンプル

上のようにCubeにはNavMeshObstacleを取り付けているので動いた部分のNavMeshのベイクされた部分が排除されています。

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

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

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

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

いやぁ、ナビゲーション機能は本当に便利ですね!

設定すれば自然なルートで移動してくれますので、敵が巡回しているようなAIが簡単に作成出来ます。

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

Unityのナビゲーション機能を使って決まった場所を巡回させる機能を作成していきます。

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

スポンサーリンク

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

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

コメント

  1. 名無し より:

    いつも参考にさせて頂いてます、とても感謝しています。
    返答ありがとうございました。

  2. 名無し より:

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