Unityのナビゲーション機能で移動エリアの可・不可を設定する

ナビゲーション機能を使った移動は出来るようになりましたが、NavigationStaticにチェックを入れてBakeしたエリアはすべて移動が可能でした。

これはエージェントが移動できるエリアをBakeする時にすべてのナビゲーションのエリアをWalkableにしたままだったからです。

ナビゲーションのエリアを作成すると移動にかかるコストを設定出来るので、最短距離で行くとコストがかかってしまう場合は迂回して別のルートを通るようになります。

移動出来るエリアと移動出来ないエリアはエージェントのレイヤー設定(ナビゲーション機能独自のレイヤー)で指定する事が出来るので、エージェントの設定で『あるエリアには入れないキャラクター』を作成するという事も可能です。

今回は移動出来るエリア、移動出来ないエリア、移動コストが高いエリアを作成し、エージェント(ナビゲーション機能で移動するキャラ)がどう移動するのか確認してみます。

エージェントの移動に関しては

Unityで主人公をナビゲーション機能とマウスクリックで移動させる
Unityでキャラクターはナビゲーション機能で移動させるが、移動先の指定はマウスクリックで行えるようにします。ナビゲーション機能を使っているので障害物を避けて最適な経路で目的地に進んでくれます。

で作った機能を使います。

スポンサーリンク

ナビゲーションで使用するエリアを作成し設定する

まずはナビゲーションで移動するエリアを作成していきます。
3D ObjectのCubeを使い道と坂を作成していきます。

ナビゲーションエリア1

上のようにCubeのサイズを調整し作成します。

名前が同じで分かりづらいですが適当に足場を繋げるだけで大丈夫です。
名前を付けているのは大元のフィールドとドアの横の壁、ドアだけです。

ドアの作成方法は

Unityで家の扉の開閉が出来るようにする
Unityのゲームに家を配置してキャラクターがその家のドアを開閉する事が出来るようにします

を参照してください。

ナビゲーションエリア2

上が出来上がった足場です。

青色部分が通常のエリアでWalkable、赤色部分がコストがかかるHardRoad、黒色部分が主人公キャラが入れないNotCharaに設定する部分です。
色はそれぞれのゲームオブジェクトにMaterialを設定し色を変更しています。

エリアはこれから作成し設定します。

ナビゲーションエリア3

Navigationタブを開きAreasをクリックすると上のような画面が開きます。
Built-inの0~2はあらかじめ設定されているエリアで3からが自分で作成出来るエリアになります。
ここで

HardRoadはコストを10
NotCharaはコストを1
Doorはコストを1

として作成してください。今回はサンプルの為に必要と思われないエリア作成をしていますが、おそらく今回作成するサンプルならば3つエリアを作る必要はありません。具体的にはNotCharaエリアがいらんかな。

Built-inのエリアで代用は可能かと思います。
とりあえずグラブる!(違・・・・)、とりあえずエリアの作成も練習出来るのでいいかな・・・。

エリアの作成が終わったので、さきほど作った足場にエリアを設定していきます。

ナビゲーションエリア4

足場の赤色の部分を選択し(Cube7)、NavigationタブのObjectをクリックしてNavigation Areaの部分をHardRoadに変更します。

これで赤色の足場はコスト10のHardRoadエリアに設定されました。
同じように黒色の部分にNotCharaを設定します。

ナビゲーションメッシュのベイクとエージェントのエリアマスクの設定

ここまで出来たらStage2以下のインスペクタですべてstaticにチェックを入れ足場をBakeします。

ナビゲーションエリア5

上のような感じでベイク出来ました。

足場の色が少し薄い部分がエージェントが移動出来るエリアになります。
一部ベイクしたエリアが見えませんがちゃんとベイクされています。

足場の端の方までベイク出来ていませんが、もっと端の方まで領域を広げたい場合は、

ナビゲーションエリア8

上のAgent Radiusの値を小さくすると端の方まで広がります。
エージェントの移動出来る場所が出来たので、次は主人公キャラクターに追加しているNavMeshAgentの設定を変更します。

ナビゲーションエリア6

上のようにNavMeshAgentコンポーネントのArea Maskの右のEveryThingをクリックします。

ナビゲーションエリア7

NotCharaの部分をクリックしチェックを外します。
Area Maskの部分がMixed …に変更されます。

これで主人公キャラクターはNotCharaに設定されたエリアに進入する事が出来なくなりました。
このArea Maskで進入可能エリアを設定出来るので、ある一定のエリアしか移動させたくない敵キャラ等を作成する時に便利です。

エージェントの動作を確認するが、ドアの通過で問題点!?

これで設定が完了したのでUnityの実行ボタンを押して確認してみます。

ナビゲーションエリア9

上の動画のように初めはNotCharaである黒色部分をクリックしますが、このエージェントはNotCharaエリアは進入出来ないようにしたので進入せず手前で止まります。

次にNotCharaエリアの下の高くなっている青色部分をクリックしキャラクターを移動させますが、赤色のHardRoadエリアはコストが高い為、赤い坂を登らず迂回して青い坂を登って目的地に移動しています。

そのエリアを通るコストを計算し、勝手に経路を探索してくれるのでナビゲーション機能恐るべし!!ですね

次はドアの部分を通過させてみましょう。
ドアには近くによると開くように設定しておきます。

ナビゲーションエリア10

え!?
ドアの奥をクリックし移動させようとしたら、ドアは開かないしキャラクターも迂回しようとしています。

ドアが開かないのはBakeする時にstaticにしたからです。

staticというのはそもそも静的なもの(動かないもの)なのでアニメーションも働かなくなってしまいます。
staticにチェックを入れずNavigation Staticだけチェックを入れるとドアが開くアニメーションは行われます。

が!

ドアの先のエリアに行かず、キャラクターは迂回しようとしてしまいます。
なぜコストは同じなのに迂回しようとしてしまうのか?

その原因は

ナビゲーションエリア11

上のようにエージェントが移動出来るエリアが途切れています。

これはドアをstaticにしてBakeしたので(Navigation Staticのみでも同じ)途切れてしまいます。
というわけで、Door以下のstaticのチェックを外して再度Bakeします。

ナビゲーションエリア12

上のようにドアの下にもエージェントが通れる場所が出来ました。
確認してみましょう。

ナビゲーションエリア13

おお!
見事にドアも開き、ドアの奥にスムースに移動が完了しました。
めでたしめでたし・・・・・・

と、都合良く出来上がったらいいんですが、そうはまいりません。
上の動画のように主人公キャラが近づいたら必ず開く自動ドアのようなものであれば問題ないんですが、ドアを鍵を使ってあけるまでは開かないようにした場合に問題が出てきます。

ドアを開いていたスクリプトをオフにして、キャラクターを動かしてみましょう。

ナビゲーションエリア14

ドアがあるはずなのにキャラクターがすり抜けていっています。

この原因はドアのstaticのチェックを外してBakeしたからで、エージェントにとっては障害物として認識されなくなった為です。
・・・・staticを入れるとドアが開いても通れないが、staticを外すとドアが閉じていても通ってしまう。

負のスパイラルですね。

ナビゲーション機能を使ってドアを通過する時の不具合を二つの方法で解決

これを解決していきます。
二つの解決方を作成してみます。

まずDoor以下のstaticのチェックを外しBakeする。
これは二つの方法どちらにも共通します。

では一つ目の方法です。

DoorのインスペクタでAdd Component→Navigation→NavMeshObstacleを追加し、ドアの大きさと同じサイズにします。
NavMeshObstacleはエージェントが障害物として認識する機能です。

ナビゲーションエリア15

上のように設定(ご自分のドアのサイズに調整してください)し、ドアの部分は通れないようにします。

ナビゲーションエリア16

上のようにドアの部分で遮られ先に進めないようになりました。

ただ、障害物を避けようとするだけで永遠に目的地を目指して進みますから、ドア付近で蠢く事になります。
これをやめさせるにはドア付近に主人公検知エリアを作成し、ドアが通れないようであればエージェントを止めるもしくはエージェントの目的地をドアの前に変更してしまうというのもいいかもしれません。

二つ目の方法は主人公が進入出来ないエリアをドアの下に作成する方法です。

ナビゲーションエリア17

上の画像のようにドアの下にDoorAreaというゲームオブジェクトを作成します。
Doorが開くと一緒に回転してしまう為、Doorの子要素にはしないでください。

ナビゲーションエリア18

DoorAreaのNavigationAreaはDoorに設定します。
その後Bakeします。

ナビゲーションエリア19

次に主人公キャラのNavMeshAgentのAreaMaskのDoorのチェックを外します。
Doorのチェックを外した事で主人公はドアの下のエリアを通る事が出来なくなりましたのでドアの先をクリックすると、(移動出来るルートがあれば)迂回して移動しようとします。

しかしこのままだとドアが開いても主人公はDoorエリアを通る事は出来ませんので、スクリプトでArea Maskを操作する必要があります。

ドアを開くスクリプトOpenDoorを修正します。(ドアを開いていたスクリプト)

ドアを開けるアニメーションにした後、エージェントに設定しているNavMove(主人公操作スクリプト)のOpenDoor関数を実行させます。

OpenDoor関数ではエージェントのArea Maskを変更します。
Area MaskはエージェントのareaMaskプロパティを変更することで設定し直す事が出来ます。

NavMesh.GetAreaFromName(Areaの名前)でナビゲーションの指定された名前のレイヤー番号が取得出来ます。
1をそのレイヤー番号分だけ左にシフトするとレイヤーマスクが得られます。

|=

とすると元々のArea Maskに指定したレイヤーも追加出来ます。

これ以降レイヤーマスクのビット演算に関しての記述がありますが興味がない方は飛ばしてください

1をレイヤー番号分だけ左にシフトさせるとレイヤーマスクが得られます。
例えばWalkable(0)、Not Walkable(1)、Jump(2)、HardRoad(3)のレイヤーマスクを2進数で表すと

これを | というビット演算子でそれぞれのビットを計算します。
| はビットでどちらかが1であれば1と計算するのでこの4つのレイヤーのレイヤーマスクは

1111

となります。これを10進数に戻すと

(2×2×2) + (2×2) + (2) + (1) = 15

となります。
この数値をagent.areaMaskに指定するとエージェントが歩けるエリアを指定出来ます。

レイヤーマスクのビット演算に関してはここまで 

これでエージェントのArea MaskのDoorにチェックが入り、Doorエリアにも進入出来るようになります。

ナビゲーションエリア20

上のようにドアが開くとその先に進む事が出来るようになりました。

動画にもありますが、こちらの方法だとドアが開く前にドアの先をクリックするとDoorが進入不可エリアなので、迂回して移動しようとしてしまいます。
ドアを開いたあとにドアの先をクリックすれば移動出来ます。

二つの方法とも問題がありますが、NavMeshObstacleを使用し検知エリアでドアの開閉状態に応じてエージェントの動きを止めるのが一番うまいやり方かもしれません。

これでUnityのナビゲーション機能で移動エリアの可・不可を設定する事が出来ました。
画像を多く使う必要があったので一苦労・・・・。
このページ読み込むのに時間かかりそうですね・・・・(ToT)/~~~