前回、僕はNavMesh学んだ。
出来るようになったことと言えば、NavMeshをBakeし、BakeされたNavMesh上でオブジェクトを動かすことだけ。
これで、僕はNavMeshを使えるようになったと言えるだろうか。
……いや、そんなことはない。
その程度のことでNavMeshの何が分かっているのだと、僕を蔑む声が聞こえる。
その声をかき消すためにも、今日も涙を拭いながら勉強を始めるとします。
この記事にはUnity2017.2.0f3を使用しています。
前回学んだこと
とりあえず、焼いて、設定して、移動することまでは出来た。
今回もNavMeshAgent
を使って移動する処理は必要になるので用意しておこう。
ObjectController.cs
using UnityEngine; using UnityEngine.AI; [RequireComponent( typeof( NavMeshAgent ) )] public class ObjectController : MonoBehaviour { [SerializeField] private Transform m_target = null; private NavMeshAgent m_navAgent = null; private void Awake() { m_navAgent = GetComponent<NavMeshAgent>(); } private void Start() { if( m_target != null ) { m_navAgent.destination = m_target.position; } } } // class ObjectController
NavMeshObstacleで作る障害物
今回は障害物を作成していきたいと思う。
障害物をどのように作ればいいのかは、すでに俺の頭に入っている。
そう、NavMeshObstacle
を使えばいいのだ。
docs.unity3d.com
NavMeshObstacle
を使うことで障害物を設定できるという、一般の人では見逃してしまうようなドキュメントの記事を俺の複写眼(アルファ・スティグマ)は見逃さなかった。
早速使ってみよう。
うん、思っていた動きと違うな。
障害物として機能はしているためようだが、通れないとわかっているのなら別の道を通って先に進んでもらいたいものだ。
これがUniteの動画でもあった、グローバルとローカルというやつなのだろうか。
docs.unity3d.com
これでは駄目だ。修正せねば。
Carve
NavMeshObstacle
の設定できるパラメータの中にCarve
というものを俺の複写眼(アルファ・スティグマ)が見つけた。
なんだ、曲がりでもするのか。
※
Carve [他動詞]〔木や石などを〕彫る、彫刻する
Curve [他動詞] ~を曲げる
英辞郎 on the WEB (https://eow.alc.co.jp/) より
なんか知らんが使ってみよう。
おお、素晴らしい。NavMeshに穴が空いたではないか!
なるほど、空間を捻じ曲げるというプロパティだったわけか。
※
Carve [他動詞]〔木や石などを〕彫る、彫刻する
Curve [他動詞] ~を曲げる
英辞郎 on the WEB (https://eow.alc.co.jp/) より
では早速この状態で実行してみるとしよう。
これこそ俺の望んでいた挙動だ。
見事障害物が完成した。
移動する障害物
止まった障害物は完成した。
ただ、中には障害物のくせに動くものだって存在するという事実がある。
ヨドバシカメラの前のスクランブル交差点を行き交う人々は、動く障害物といっても過言ではないだろう。
というわけで移動するためのスクリプトを作るとしよう。
MovingObstacle.cs
using UnityEngine; using System.Collections; public class MovingObstacle : MonoBehaviour { [SerializeField] private Vector3 m_targetPosition = Vector3.zero; [SerializeField] private float m_moveTime = 0.0f; [SerializeField] private float m_waitTime = 0.0f; private void Start() { StartCoroutine( Move( m_targetPosition ) ); } private IEnumerator Move( Vector3 i_target ) { Vector3 startPosition = transform.position; float startTime = Time.time; float endTime = startTime + m_moveTime; while( Time.time < endTime ) { float elapsedTime = Time.time - startTime; float elapsedRate = elapsedTime / m_moveTime; Vector3 pos = Vector3.Lerp( startPosition, i_target, elapsedRate ); transform.position = pos; yield return null; } transform.position = i_target; yield return new WaitForSeconds( m_waitTime ); StartCoroutine( Move( startPosition ) ); } } // class MovingObstacle
ただ、最初の位置と指定した位置を往復するだけの簡単なスクリプトだ。
目的地に止まったら一定時間その場で止まるという機能もつけてみた。
これを使って障害物の挙動を見てみようじゃないか。
うむ、目的通りの動きをしてくれておる。
ただ、いくつかCarve
を設定した際に追加されたパラメータがある。
折角なので見ていこう。
Move Threshold
Move Threshold
を俺の複写眼(アルファ・スティグマ)が見つけた。
移動の閾値的なものだろうか、せっかくなので大きい数値にしてみよう。
この状態で実行するとどうなるだろうか。
先ほどのものと比べてみても、NavMeshの穴の移動が大まかなものになることがわかった。
場所によっては、明らかにオブジェクトのサイズ分穴が空いてない。
NavMeshに穴を連続して空けていくのは、決して軽い処理でもあるまい。
今の障害物動きのような場合は、数値を小さくする必要がありそうだが、一気に場所を移動する場合などは、大きい数値でもかまわないだろう。
Carve Only Stationary
Carve Only Stationary
を俺の複写眼(アルファ・スティグマ)が見つけた。
ふむ、チェックボックスのようだが……。
とりあえず有効にして実行してみよう。
障害物が止まっている場合のみ、NavMeshに穴が空くようになった。
なるほど、こんなこともNavMeshObstacle
は出来るわけか。
Time To Stationary
Time To Stationary
。
もはや複写眼(アルファ・スティグマ)を使う必要もない。
恐らくこれは、「この数値以上動かなかったら止まる扱いになる」というものであろう。
Carve Only Stationary
とセットで使うものとみた。
待機時間よりTime To Stationary
を大きな数値にして確かめてみる。
予想通り。
NavMesh、俺の勝ちだ。
最後に
続きました。