【Unity】NavMeshAgentがアイテムを拾って拠点まで運ばせる方法

どうも、だらはです。
NavMeshAgentがアイテムを拾って拠点まで運ばせる方法についてまとめたいと思います。

スポンサーリンク

方法

先ず手法について、地道にスクリプトで実装する方法になります。
FixedJointという機能を使えばオブジェクトとオブジェクトを結合することができるのですがNavMeshAgentとは併用ができませんでした。
(FixedJointRigidBodyを使うためNavMeshAgentとの挙動に弊害が生じる)

本記事で紹介するスクリプトにて、アイテムをNavMeshAgentの上に持ち上げ、そのまま移動させることができます。
以下図を参考にして、地形とアイテム、NavMeshAgentを作成してください。

補足

  • Unityバージョン6以降では、NavMeshSurfaceBakeすることになったようです。
  • Itemには、Itemタグを設定してください。
  • NavMeshAgentQualityNoにするとNavMeshAgent同士が衝突しなくなります。

スクリプト(AgentController.cs)


using UnityEngine;
using UnityEngine.AI;

public class AgentController : MonoBehaviour
{
    public Transform homeBase; // 拠点のTransform
    NavMeshAgent agent;
    ItemController targetItem;
    bool carryingItem = false;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if (carryingItem)
        {
            if (agent != null && homeBase != null)
            {
                // 拠点へ向かう
                agent.SetDestination(homeBase.position);
            }
        }
        else
        {
            // アイテムを探す
            FindAndAssistItem();
        }
    }

    void FindAndAssistItem()
    {
        // アイテムを見つける(最も近いアイテムを探す)
        GameObject[] items = GameObject.FindGameObjectsWithTag("Item");
        float closestDistance = Mathf.Infinity;
        GameObject closestItem = null;

        foreach (GameObject item in items)
        {
            float distance = Vector3.Distance(transform.position, item.transform.position);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestItem = item;
            }
        }

        if (closestItem != null)
        {
            targetItem = closestItem.GetComponent<ItemController>();
            if (targetItem != null)
            {
                agent.SetDestination(targetItem.transform.position);
            }
        }
    }

    void OnTriggerEnter(Collider other)
    {
        var itemController = other.gameObject.GetComponent<ItemController>();
        if (itemController != null && itemController == targetItem)
        {
            targetItem.AddCarrier(this);
            carryingItem = true;
        }
    }

    void OnTriggerExit(Collider other)
    {
        var itemController = other.gameObject.GetComponent<ItemController>();
        if (itemController != null && itemController == targetItem)
        {
            targetItem.RemoveCarrier(this);
            carryingItem = false;
        }
    }
}

スクリプト(ItemController.cs)

using UnityEngine;
using System.Collections.Generic;

public class ItemController : MonoBehaviour
{
    public Transform homeBase; // 拠点のTransform
    List<AgentController> carryingAgents = new List<AgentController>();
    bool isCarried = false;

    void Update()
    {
        if (isCarried && carryingAgents.Count > 0)
        {
            Vector3 averagePosition = Vector3.zero;
            foreach (var agent in carryingAgents)
            {
                averagePosition += agent.transform.position;
            }
            averagePosition /= carryingAgents.Count;

            // エージェントの平均位置にアイテムを移動
            transform.position = averagePosition + new Vector3(0, 1, 0);

            // 拠点までの移動をチェック
            if (Vector3.Distance(transform.position, homeBase.position) < 1.0f)
            {
                DropItem();
            }
        }
    }

    public void AddCarrier(AgentController agent)
    {
        carryingAgents.Add(agent);
        if (!isCarried)
        {
            isCarried = true;
        }
    }

    public void RemoveCarrier(AgentController agent)
    {
        carryingAgents.Remove(agent);
        if (carryingAgents.Count == 0)
        {
            isCarried = false;
        }
    }

    void DropItem()
    {
        isCarried = false;
        carryingAgents.Clear();
        // アイテムを拠点に配置するなどの処理
    }
}

まとめ

この手法では、複数のNavMeshAgentがアイテムを運ぶことができます。
これで〇クミンが作れるぞ!!(←おい)

以上、だらはでした。

スポンサーリンク

応用

Posted by daraha_gm