しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity で NavMeshAgent を使ってキャラを動かす

※ これは 2021/03/12 時点の Unity 2020.3.0f1 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

前回は生成した NavMesh にしたがってねこキャラを動かしてみたが、追いかけるねこも同じようにちゃんと箱を避けるようにしたい

前回と同じように NavMesh.CalculatePath() 使ってもいいけど、今回はねこキャラ同士も当たり判定をつけたいので NavMeshAgent を利用する

docs.unity3d.com

NavMeshAgent は現状 3D 用で 2D 用途には対応していないため、ねこキャラの GameObject に直接アタッチせずに 座標だけ同期するようにする

そのためにまずは下記のような NavMeshAgent の Prefab を作成

NavMeshAgent プレハブ

といっても NavMeshAgent をアタッチして Radius を調整しただけ

次に Cat.cs を下記のように変更

using UnityEngine;
using UnityEngine.AI;

public class Cat : MonoBehaviour
{
    /// <summary>
    /// NavMesh エージェント
    /// </summary>
    public NavMeshAgent Agent { get; set; } = null;

    private Animator animator = null;

    public void Awake()
    {
        this.animator = this.GetComponent<Animator>();
    }

    public void Update()
    {
        // NavMesh エージェント設定待ち
        if (this.Agent == null)
        {
            return;
        }

        var direction = (Vector2)this.Agent.velocity.normalized;
        if (direction != Vector2.zero)
        {
            var normalized = new Vector2(Mathf.Round(direction.x), Mathf.Round(direction.y));

            // 斜め方向も許容
            if (normalized != Vector2.zero)
            {
                this.animator.SetFloat("x", normalized.x);
                this.animator.SetFloat("y", normalized.y);
            }
            var position = this.Agent.transform.position;
            this.transform.position = (Vector2)position;
        }
    }
}

目的地への経路計算はほぼ NavMeshAgent に任せる方式にした

合わせて MainView.cs も変更

using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;

public class MainView : MonoBehaviour
{
    [SerializeField]
    private GameObject tapEffect = null;

    [SerializeField]
    private GameObject agentPrefab = null;

    [SerializeField]
    private Cat cat = null;

    [SerializeField]
    private Cat chaser = null;

    private NavMeshAgent catAgent = null;
    private NavMeshAgent chaserAgent = null;

    public void Start()
    {
        // NavMeshAgent を生成し、ねこキャラと連携させる
        this.catAgent = Instantiate(this.agentPrefab, this.cat.transform.position, Quaternion.identity, this.transform)
            .GetComponent<NavMeshAgent>();
        this.chaserAgent = Instantiate(this.agentPrefab, this.chaser.transform.position, Quaternion.identity, this.transform)
            .GetComponent<NavMeshAgent>();

        this.cat.Agent = this.catAgent;
        this.chaser.Agent = this.chaserAgent;
    }

    public void Update()
    {
        // ねこキャラを追いかけさせる
        this.chaser.Agent.SetDestination(this.cat.transform.position);
    }

    public void OnFire(InputAction.CallbackContext context)
    {
        var target = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
        this.cat.Agent.SetDestination(target);

        // タップ跡を表示
        var effect = Instantiate(this.tapEffect, (Vector2)target, Quaternion.identity, this.transform);

        // 1秒後に消滅
        Destroy(effect, 1f);
    }
}

追いかけるねこキャラも Cat.cs を設定するようにして、こちらで目的地を制御する方法に変更してスッキリ

NavMeshAgent.SetDestination() で目的地を設定している

Chaser.cs を設定していた GameObjectCat.cs を設定するように差し替え

Chaser も Cat.cs に差し替え

最後に MainView の SerializeField を適切に設定

MainView の SerializeField を設定

これで動かしてみると・・・

NavMeshAgent で動かす

ちゃんとねこキャラ同士も避けるようになった!