徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【Unity】NavMeshを学ぶ 障害物編

 前回、僕はNavMesh学んだ。
 出来るようになったことと言えば、NavMeshをBakeし、BakeされたNavMesh上でオブジェクトを動かすことだけ。
 これで、僕はNavMeshを使えるようになったと言えるだろうか。
 ……いや、そんなことはない。
 その程度のことでNavMeshの何が分かっているのだと、僕を蔑む声が聞こえる。

 その声をかき消すためにも、今日も涙を拭いながら勉強を始めるとします。


この記事にはUnity2017.2.0f3を使用しています。

前回学んだこと

 とりあえず、焼いて、設定して、移動することまでは出来た。

www.urablog.xyz

 今回もNavMeshAgentを使って移動する処理は必要になるので用意しておこう。

f:id:urahimono:20171014184829p:plain 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

f:id:urahimono:20171014184845g:plain

NavMeshObstacleで作る障害物

 今回は障害物を作成していきたいと思う。
 障害物をどのように作ればいいのかは、すでに俺の頭に入っている。
 そう、NavMeshObstacleを使えばいいのだ。
 
docs.unity3d.com

 NavMeshObstacleを使うことで障害物を設定できるという、一般の人では見逃してしまうようなドキュメントの記事を俺の複写眼(アルファ・スティグマ)は見逃さなかった。

docs.unity3d.com

 早速使ってみよう。

f:id:urahimono:20171014184948p:plain
f:id:urahimono:20171014185023g:plain

 うん、思っていた動きと違うな。

f:id:urahimono:20171014185345p:plain

 障害物として機能はしているためようだが、通れないとわかっているのなら別の道を通って先に進んでもらいたいものだ。
 これがUniteの動画でもあった、グローバルとローカルというやつなのだろうか。
 
docs.unity3d.com

 これでは駄目だ。修正せねば。

Carve

 NavMeshObstacleの設定できるパラメータの中にCarveというものを俺の複写眼(アルファ・スティグマ)が見つけた。

 なんだ、曲がりでもするのか。


Carve [他動詞]〔木や石などを〕彫る、彫刻する
Curve [他動詞] ~を曲げる
英辞郎 on the WEB (https://eow.alc.co.jp/) より

 なんか知らんが使ってみよう。

f:id:urahimono:20171014185503p:plain

 おお、素晴らしい。NavMeshに穴が空いたではないか!
 なるほど、空間を捻じ曲げるというプロパティだったわけか。


Carve [他動詞]〔木や石などを〕彫る、彫刻する
Curve [他動詞] ~を曲げる
英辞郎 on the WEB (https://eow.alc.co.jp/) より

 では早速この状態で実行してみるとしよう。

f:id:urahimono:20171014185600g:plain
f:id:urahimono:20171014185651p:plain

 これこそ俺の望んでいた挙動だ。
 見事障害物が完成した。

移動する障害物

 止まった障害物は完成した。
 ただ、中には障害物のくせに動くものだって存在するという事実がある。
 ヨドバシカメラの前のスクランブル交差点を行き交う人々は、動く障害物といっても過言ではないだろう。

 というわけで移動するためのスクリプトを作るとしよう。

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

 ただ、最初の位置と指定した位置を往復するだけの簡単なスクリプトだ。
 目的地に止まったら一定時間その場で止まるという機能もつけてみた。
 これを使って障害物の挙動を見てみようじゃないか。

f:id:urahimono:20171014185723p:plain
f:id:urahimono:20171014185737g:plain

 うむ、目的通りの動きをしてくれておる。
 ただ、いくつかCarveを設定した際に追加されたパラメータがある。
 折角なので見ていこう。

Move Threshold

 Move Thresholdを俺の複写眼(アルファ・スティグマ)が見つけた。
 移動の閾値的なものだろうか、せっかくなので大きい数値にしてみよう。

f:id:urahimono:20171014185859p:plain

 この状態で実行するとどうなるだろうか。

f:id:urahimono:20171014185913g:plain

 先ほどのものと比べてみても、NavMeshの穴の移動が大まかなものになることがわかった。
 場所によっては、明らかにオブジェクトのサイズ分穴が空いてない。
 NavMeshに穴を連続して空けていくのは、決して軽い処理でもあるまい。
 今の障害物動きのような場合は、数値を小さくする必要がありそうだが、一気に場所を移動する場合などは、大きい数値でもかまわないだろう。

Carve Only Stationary

 Carve Only Stationaryを俺の複写眼(アルファ・スティグマ)が見つけた。
 ふむ、チェックボックスのようだが……。
 とりあえず有効にして実行してみよう。

f:id:urahimono:20171014190021p:plain
f:id:urahimono:20171014190031g:plain

 障害物が止まっている場合のみ、NavMeshに穴が空くようになった。
 なるほど、こんなこともNavMeshObstacleは出来るわけか。

Time To Stationary

 Time To Stationary
 もはや複写眼(アルファ・スティグマ)を使う必要もない。
 恐らくこれは、「この数値以上動かなかったら止まる扱いになる」というものであろう。
 Carve Only Stationaryとセットで使うものとみた。

 待機時間よりTime To Stationaryを大きな数値にして確かめてみる。

f:id:urahimono:20171014190116p:plain
f:id:urahimono:20171014190130g:plain

 予想通り。
 NavMesh、俺の勝ちだ。

最後に

 続きました。

www.urablog.xyz