ナビゲーション機能を使った移動は出来るようになりましたが、NavigationStaticにチェックを入れてBakeしたエリアはすべて移動が可能でした。
これはエージェントが移動できるエリアをBakeする時にすべてのナビゲーションのエリアをWalkableにしたままだったからです。
ナビゲーションのエリアを作成すると移動にかかるコストを設定出来るので、最短距離で行くとコストがかかってしまう場合は迂回して別のルートを通るようになります。
移動出来るエリアと移動出来ないエリアはエージェントのレイヤー設定(ナビゲーション機能独自のレイヤー)で指定する事が出来るので、エージェントの設定で『あるエリアには入れないキャラクター』を作成するという事も可能です。
今回は移動出来るエリア、移動出来ないエリア、移動コストが高いエリアを作成し、エージェント(ナビゲーション機能で移動するキャラ)がどう移動するのか確認してみます。
エージェントの移動に関しては

で作った機能を使います。
またナビゲーション機能でドアを開く時にキャラクターを検知しなくてはいけない為、キャラクターにはRigidbodyのIs Kinematicにチェックを入れたものとコライダを取り付けておきます。
ナビゲーションで使用するエリアを作成し設定する
まずはナビゲーションで移動するエリアを作成していきます。
3D ObjectのCubeを使い道と坂を作成していきます。
上のようにCubeのサイズを調整し作成します。
名前が同じで分かりづらいですが適当に足場を繋げるだけで大丈夫です。
名前を付けているのは大元のフィールドとドアの横の壁、ドアだけです。
ドアの作成方法は

を参照してください。
上が出来上がった足場です。
青色部分が通常のエリアでWalkable、赤色部分がコストがかかるHardRoad、黒色部分が主人公キャラが入れないNotCharaに設定する部分です。
色はそれぞれのゲームオブジェクトにMaterialを設定し色を変更しています。
エリアはこれから作成し設定します。
Navigationタブを開きAreasをクリックすると上のような画面が開きます。
Built-inの0~2はあらかじめ設定されているエリアで3からが自分で作成出来るエリアになります。
ここで
HardRoadはコストを10
NotCharaはコストを1
Doorはコストを1
として作成してください。今回はサンプルの為に必要と思われないエリア作成をしていますが、おそらく今回作成するサンプルならば3つエリアを作る必要はありません。具体的にはNotCharaエリアがいらんかな。
Built-inのエリアで代用は可能かと思います。
とりあえずグラブる!(違・・・・)、とりあえずエリアの作成も練習出来るのでいいかな・・・。
エリアの作成が終わったので、さきほど作った足場にエリアを設定していきます。
足場の赤色の部分を選択し(Cube7)、NavigationタブのObjectをクリックしてNavigation Areaの部分をHardRoadに変更します。
これで赤色の足場はコスト10のHardRoadエリアに設定されました。
同じように黒色の部分にNotCharaを設定します。
ナビゲーションメッシュのベイクとエージェントのエリアマスクの設定
ここまで出来たらStage2以下のインスペクタですべてstaticにチェックを入れ足場をBakeします。
上のような感じでベイク出来ました。
足場の色が少し薄い部分がエージェントが移動出来るエリアになります。
一部ベイクしたエリアが見えませんがちゃんとベイクされています。
足場の端の方までベイク出来ていませんが、もっと端の方まで領域を広げたい場合は、
上のAgent Radiusの値を小さくすると端の方まで広がります。
エージェントの移動出来る場所が出来たので、次は主人公キャラクターに追加しているNavMeshAgentの設定を変更します。
上のようにNavMeshAgentコンポーネントのArea Maskの右のEveryThingをクリックします。
NotCharaの部分をクリックしチェックを外します。
Area Maskの部分がMixed …に変更されます。
これで主人公キャラクターはNotCharaに設定されたエリアに進入する事が出来なくなりました。
このArea Maskで進入可能エリアを設定出来るので、ある一定のエリアしか移動させたくない敵キャラ等を作成する時に便利です。
エージェントの動作を確認するが、ドアの通過で問題点!?
これで設定が完了したのでUnityの実行ボタンを押して確認してみます。
上の動画のように初めはNotCharaである黒色部分をクリックしますが、このエージェントはNotCharaエリアは進入出来ないようにしたので進入せず手前で止まります。
次にNotCharaエリアの下の高くなっている青色部分をクリックしキャラクターを移動させますが、赤色のHardRoadエリアはコストが高い為、赤い坂を登らず迂回して青い坂を登って目的地に移動しています。
そのエリアを通るコストを計算し、勝手に経路を探索してくれるのでナビゲーション機能恐るべし!!ですね
次はドアの部分を通過させてみましょう。
ドアには近くによると開くように設定しておきます。
え!?
ドアの奥をクリックし移動させようとしたら、ドアは開かないしキャラクターも迂回しようとしています。
ドアが開かないのはBakeする時にstaticにしたからです。
staticというのはそもそも静的なもの(動かないもの)なのでアニメーションも働かなくなってしまいます。
staticにチェックを入れずNavigation Staticだけチェックを入れるとドアが開くアニメーションは行われます。
が!
ドアの先のエリアに行かず、キャラクターは迂回しようとしてしまいます。
なぜコストは同じなのに迂回しようとしてしまうのか?
その原因は
上のようにエージェントが移動出来るエリアが途切れています。
これはドアをstaticにしてBakeしたので(Navigation Staticのみでも同じ)途切れてしまいます。
というわけで、Door以下のstaticのチェックを外して再度Bakeします。
上のようにドアの下にもエージェントが通れる場所が出来ました。
確認してみましょう。
おお!
見事にドアも開き、ドアの奥にスムーズに移動が完了しました。
めでたしめでたし・・・・・・
と、都合良く出来上がったらいいんですが、そうはまいりません。
上の動画のように主人公キャラが近づいたら必ず開く自動ドアのようなものであれば問題ないんですが、ドアを鍵を使ってあけるまでは開かないようにした場合に問題が出てきます。
ドアを開いていたスクリプトをオフにして、キャラクターを動かしてみましょう。
ドアがあるはずなのにキャラクターがすり抜けていっています。
この原因はドアのstaticのチェックを外してBakeしたからで、エージェントにとっては障害物として認識されなくなった為です。
・・・・staticを入れるとドアが開いても通れないが、staticを外すとドアが閉じていても通ってしまう。
負のスパイラルですね。
ナビゲーション機能を使ってドアを通過する時の不具合を二つの方法で解決
これを解決していきます。
二つの解決方を作成してみます。
まずDoor以下のstaticのチェックを外しBakeする。
これは二つの方法どちらにも共通します。
NavMeshObstacleをドアに取り付ける
では一つ目の方法です。
Doorの子要素のCube(実体)のインスペクタでAdd Component→Navigation→NavMeshObstacleを追加し、ドアの大きさと同じサイズにします。
NavMeshObstacleはエージェントが障害物として認識する機能です。
上のように設定(ご自分のドアのサイズに調整してください)し、ドアの部分は通れないようにします。
上のようにドアの部分で遮られ先に進めないようになりました。
キャラクターが蠢いていますが、NavMeshのBakeがうまくいってなかったのかも?
ドアの下に侵入出来ないエリアを作成する
二つ目の方法は主人公が進入出来ないエリアをドアの下に作成する方法です。
上の画像のようにドアの下にDoorAreaというゲームオブジェクトを作成します。
Doorが開くと一緒に回転してしまう為、Doorの子要素にはしないでください。
DoorAreaのNavigationAreaはDoorに設定します。
その後Bakeします。
次に主人公キャラのNavMeshAgentのAreaMaskのDoorのチェックを外します。
Doorのチェックを外した事で主人公はドアの下のエリアを通る事が出来なくなりましたのでドアの先をクリックすると、(移動出来るルートがあれば)迂回して移動しようとします。
しかしこのままだとドアが開いても主人公はDoorエリアを通る事は出来ませんので、スクリプトでArea Maskを操作する必要があります。
ドアを開くスクリプトOpenDoorを修正します。(ドアを開いていたスクリプト)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using UnityEngine; using System.Collections; public class OpenDoorTest : MonoBehaviour { private Animator animator; void Start () { animator = GetComponent<Animator>(); } void OnTriggerEnter(Collider col) { if(col.tag == "Player") { // ドアを開けるアニメーション animator.SetBool("Open", true); // エージェントがドアを通れるようにする col.gameObject.GetComponent<NavMove>().OpenDoor(); } } } |
ドアを開けるアニメーションにした後、エージェントに設定しているNavMove(主人公操作スクリプト)のOpenDoor関数を実行させます。
1 2 3 4 5 | void OpenDoor() { agent.areaMask |= 1 << NavMesh.GetAreaFromName("Door") ; } |
OpenDoor関数ではエージェントのArea Maskを変更します。
Area MaskはエージェントのareaMaskプロパティを変更することで設定し直す事が出来ます。
NavMesh.GetAreaFromName(Areaの名前)でナビゲーションの指定された名前のレイヤー番号が取得出来ます。
1をそのレイヤー番号分だけ左にシフトするとレイヤーマスクが得られます。
|=
とすると元々のArea Maskに指定したレイヤーも追加出来ます。
レイヤーマスクの計算
1をレイヤー番号分だけ左にシフトさせるとレイヤーマスクが得られます。
例えばWalkable(0)、Not Walkable(1)、Jump(2)、HardRoad(3)のレイヤーインデックスを2進数で表すと
1 2 3 4 | Walkable 0001 Not Walkable 0010 Jump 0100 HardRoad 1000 |
これを | というビット演算子でそれぞれのビットを計算します。
| はビットでどちらかが1であれば1と計算するのでこの4つのレイヤーのレイヤーマスクは
1111
となります。これを10進数に戻すと
(2×2×2) + (2×2) + (2) + (1) = 15
となります。
この数値をagent.areaMaskに指定するとエージェントが歩けるエリアを指定出来ます。
動作確認
これでエージェントのArea MaskのDoorにチェックが入り、Doorエリアにも進入出来るようになります。
上のようにドアが開くとその先に進む事が出来るようになりました。
動画にもありますが、こちらの方法だとドアが開く前にドアの先をクリックするとDoorが進入不可エリアなので、迂回して移動しようとしてしまいます。
ドアを開いたあとにドアの先をクリックすれば移動出来ます。
二つの方法とも問題がありますが、NavMeshObstacleを使用し検知エリアでドアの開閉状態に応じてエージェントの動きを止めるのが一番うまいやり方かもしれません。
これでUnityのナビゲーション機能で移動エリアの可・不可を設定する事が出来ました。
画像を多く使う必要があったので一苦労・・・・。
このページ読み込むのに時間かかりそうですね・・・・(ToT)/~~~