【自作ゲーム開発10】味方NPCの標的取得【Unity】


注意
本記事で掲載されている動作の実装方法及びプログラムのソースコードは最適な方法ではない可能性があります。
今後不具合等が判明した場合には修正及び改良をおこなう可能性があります。
また今後自分で同機能を実装する場合の参考にする可能性もあるためソースコードだけではなく説明しつつ記事を進めていきます。

はじめに

Unityによる自作ゲーム開発進捗その10になります!

今回は指定位置から距離に応じたオブジェクトの取得を実装します。

自身の拠点から一番近いオブジェクトを追いかけるだけの味方コンピューターも追加します。

新しいスクリプトを2つ作成します。

以下にソースコードを掲載します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SerchObject : MonoBehaviour{
    private List<GameObject> m_object_list = new List<GameObject>();

    private SphereCollider m_serch_collider;

    private string m_tag;

    private void Start() {
        m_serch_collider = this.GetComponent<SphereCollider>();
        m_serch_collider.enabled = false;
    }

    public void SerchStart(GameObject a_area, string a_tag) {
        m_object_list.Clear();
        m_tag = a_tag;
        m_serch_collider.transform.position = a_area.transform.position;
        m_serch_collider.enabled = true;
    }

    public GameObject SerchEnd(GameObject a_area, int a_distance) { // 補足1
        m_serch_collider.enabled = false;

        if (m_object_list.Count <= 0) return null;

        int l_list_array = 0;

        if (a_distance == 0) {
            float l_distance_check = 999.0f;

            for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
                if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude < l_distance_check) {
                    l_list_array = l_loop;
                    l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
                }
            }
        } else {
            float l_distance_check = 0.0f;

            for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
                if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude > l_distance_check) {
                    l_list_array = l_loop;
                    l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
                }
            }
        }

        return m_object_list[l_list_array];
    }

    private void OnTriggerEnter(Collider a_collider) {
        if (a_collider.CompareTag(m_tag)) m_object_list.Add(a_collider.gameObject);
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    補足1 引数1のオブジェクトから近い距離か遠い距離のオブジェクトを返す(引数2が0だと近いオブジェクト)

    [バージョン]
    2021-02-25 コライダー内オブジェクトの取得と指定オブジェクトからの距離で一番近い及び遠いオブジェクトを戻り値とする関数

    [タスク]
    
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class DefensePlayer : MonoBehaviour{
    private const float m_serch_speed = 0.5f; // 補足1

    [SerializeField]
    private SerchObject m_serch_system;

    private NavMeshAgent m_navmesh_agent;
    private Animator m_animator;
    private GameObject m_target_object;
    private GameObject m_serch_object;

    private float m_time_count = 0.0f;

    private bool m_lockon_state = false;
    private bool m_serch_state = false;

    private void Start() {
        Initialize();
    }

    private void FixedUpdate() {
        if (m_lockon_state) {
            if(m_target_object.activeSelf == false) {
                m_lockon_state = false;
                m_animator.SetBool("animation_move", false);
            } else {
                m_navmesh_agent.SetDestination(m_target_object.transform.position); // タスク1
                m_animator.SetBool("animation_move", true); // タスク1
            }
        } else {
            m_time_count += Time.deltaTime;

            if ((m_time_count > m_serch_speed) && (!m_serch_state)) {
                m_serch_state = true;
                m_serch_system.SerchStart(m_serch_object, "Enemy");
            }

            if ((m_time_count > m_serch_speed * 2.0f) && (m_serch_state)) {
                m_serch_state = false;
                m_time_count = 0.0f;
                m_target_object = m_serch_system.SerchEnd(m_serch_object, 0); // タスク3

                if (m_target_object != null) {
                    m_navmesh_agent.SetDestination(m_target_object.transform.position);
                    m_lockon_state = true;
                }
            }
        }
    }

    public void Initialize() {
        m_navmesh_agent = GetComponent<NavMeshAgent>();
        m_animator = GetComponent<Animator>();
        m_serch_object = GameObject.Find("PlayerBase"); // タスク4
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    補足1 数値を下げると行動の切り替えが早くなる(強くなる)

    [バージョン]
    2021-02-25 感知エリアから一番近いオブジェクトを追いかける

    [タスク]
    タスク1 初期化は指揮官キャラが呼び出す
    タスク2 臨時の動作(対象に向かって歩くだけ)
    タスク3 攻撃キャラは自身から近い対象にする分岐を入れる
    タスク4 臨時防衛拠点
*/

距離判定によるオブジェクトの取得

public void SerchStart(GameObject a_area, string a_tag) {
    m_object_list.Clear();
    m_tag = a_tag;
    m_serch_collider.transform.position = a_area.transform.position;
    m_serch_collider.enabled = true;
}

引数1に距離を計測するオブジェクトを入っており引数2には取得したいオブジェクトのタグが入っています。

引数1の位置にコライダーを移動させてアクティブ化させています。


public GameObject SerchEnd(GameObject a_area, int a_distance) { // 補足1
    m_serch_collider.enabled = false;

    if (m_object_list.Count <= 0) return null;

    int l_list_array = 0;

    if (a_distance == 0) {
        float l_distance_check = 999.0f;

        for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
            if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude < l_distance_check) {
                l_list_array = l_loop;
                l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
            }
        }
    } else {
        float l_distance_check = 0.0f;

        for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
            if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude > l_distance_check) {
                l_list_array = l_loop;
                l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
            }
        }
    }

    return m_object_list[l_list_array];
}

上記関数では引数1に距離を計測する元の位置を入れ引数2は0か0以外を入れるようにしています。

この関数が呼ばれる際SerchStart関数起動後に指定したタグのオブジェクトがコライダー内に侵入していればリストにオブジェクトが格納されています。

引数2に0を入れると引数1から一番近いオブジェクトが戻り値となり引数2に0以外を入れると引数1から一番遠いオブジェクトが戻り値となります。


味方コンピューターの移動動作

今回から作成する味方コンピューターは味方拠点から一番近いオブジェクトを対象に戦闘するタイプにします。

m_serch_system.SerchStart(m_serch_object, "Enemy");

SerchStart関数にて味方拠点付近のEnemyのタグを持つオブジェクトを取得します。


m_target_object = m_serch_system.SerchEnd(m_serch_object, 0); // タスク3

取得したEnemyタグのオブジェクトに対して味方拠点から一番近いオブジェクトをターゲットとして取得しています。


if(m_target_object.activeSelf == false) {
    m_lockon_state = false;
    m_animator.SetBool("animation_move", false);
} else {
    m_navmesh_agent.SetDestination(m_target_object.transform.position); // タスク1
    m_animator.SetBool("animation_move", true); // タスク1
}

今回はターゲットが休息状態なら敵を探す状態に戻り、休息状態ではない場合NavMeshAgentを更新しターゲットの後ろをついていくだけの状態にしています。


今回使用するアセット


動画


記事のリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です