しっぽを追いかけて

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

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

Unity でキャラを NavMesh にしたがって動かす

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

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

前回は Tilemap に応じて NavMesh を生成してみたので、さっそくこれにしたがってねこキャラを動かしてみたい

キャラの中心点が NavMesh の範囲を通るので、こんな感じに前回生成したものより細い道になるよう、まずは NavigationAgents タブの Radius を大きな値にした

NavMesh はキャラのサイズを考慮して生成

次に Hierarchy 上の ‘CatChaserGridの GameObkect をMainView` という名前の GameObject の子どもになるように変更

Hierarchy の変更

Cat の GameObject の Component から下記のように Player Input を削除し、Tap Effect の参照もなくす

Player Input は MainView に移す

代わりに MainView の方に Player Input を追加し、後述する MainView の Script を追加し、Tap Effect の Prefab と Cat の参照を設定

MainView に全体を制御させる

Player Input の中の Fire イベントハンドラも指定する

MainView.cs は下記の通り

using UnityEngine;
using UnityEngine.InputSystem;

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

    [SerializeField]
    private Cat cat = null;

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

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

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

マウスクリック地点を受け取って CatTarget プロパティに代入する

Cat.cs の方はこんな感じに修正

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;

public class Cat : MonoBehaviour
{
    /// <summary>
    /// 最終目的地
    /// </summary>
    public Vector2 Target
    {
        get => this.target;
        set
        {
            if (this.target == value)
            {
                return;
            }
            this.target = value;
            this.Calculate();
        }
    }
    private Vector2 target = Vector2.zero;

    private Animator animator = null;
    private Queue<Vector3> corners = new Queue<Vector3>();
    private Vector2 next = default;

    public void Awake()
    {
        this.animator = this.GetComponent<Animator>();
        this.next = (Vector2)this.transform.position;
        this.Target = this.next;
    }

    private void Calculate()
    {
        // NavMesh に応じて経路を求める
        NavMeshPath path = new NavMeshPath();
        if (NavMesh.CalculatePath(this.transform.position, this.target, NavMesh.AllAreas, path))
        {
            this.corners = new Queue<Vector3>(path.corners);
            this.next = (Vector2)this.transform.position;
        }
    }

    private void GoNext()
    {
        // 次の目的地を取得する
        if (this.corners.Count < 1)
        {
            return;
        }
        this.next = this.corners.Dequeue();
    }

    public void Update()
    {
        // 目的地に近づいたら次の目的地を目指す
        var current = (Vector2)this.transform.position;
        if (Vector2.Distance(this.next, current) < 0.2f)
        {
            this.GoNext();
        }

        var direction = this.next - current;
        if (direction != Vector2.zero)
        {
            var normalized = new Vector3(Mathf.Round(direction.normalized.x), Mathf.Round(direction.normalized.y), 0);

            // 斜め方向も許容
            if (normalized != Vector3.zero)
            {
                this.animator.SetFloat("x", normalized.x);
                this.animator.SetFloat("y", normalized.y);
            }
            this.transform.Translate(direction.normalized * Time.deltaTime * 3f);
        }
    }
}

NavMesh に応じて最終目的地までの経路を計算し、順番に移動するようにした

これでお試し

箱をよけて動くねこキャラ

とりあえず白ねこの方だけ箱をよけて動くようになった!