【Unity】遠くにいる敵を自動で削除する

投稿者: | 2020-06-15

前の記事のプレイヤーをFPSコントローラーにして、移動するプレイヤーから遠くにいる敵の削除と、新しい敵の配置が自動で行われるようにしました。

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

public class NavMeshTestScript : MonoBehaviour
{
    [SerializeField][Range(0f,1f)] float t;
    [SerializeField] float dist = 30f;
    [SerializeField] GameObject sphere;
    [SerializeField] GameObject sphere2;
    [SerializeField] float divAngle = 40f; // 割る角度
    [SerializeField] float maxRadius = 30f; // 配置する敵のプレイヤーとの距離の最大値
    [SerializeField] float minRadius = 10f; // 最小値

    [SerializeField] float maxAgentDist = 50f;
    [SerializeField] float minAgentDist = 5f;

    [SerializeField] float deployInterval = 15f;
    [SerializeField] Text text;
    Vector3 prePosition;

    float sec = 0f;

    // Start is called before the first frame update
    void Start()
    {
        prePosition = transform.position;
    }

    void InstantiateAgent(float pre_angle, float delta_angle)
    {

        float d = divAngle * Mathf.Deg2Rad; // 割る角度をラジアンにする
        int i = Mathf.FloorToInt(delta_angle / d); // 割った整数部分

        // 割った整数部分-1回繰り返す
        for (int n = 1; n <= i - 1; n++)
        {
            // 小さい方の角度 + 差 *(繰り返し回数)/(割った整数部分)
            float a = pre_angle + delta_angle * n / i;

            // 基準からその角度だけ回転させたベクトル
            Vector3 newDirection = Quaternion.Euler(0f, a * Mathf.Rad2Deg, 0f) * Vector3.forward;

            // プレイヤーからその方向へ伸ばした位置に青い球を生成
            Vector3 pos = newDirection * UnityEngine.Random.Range(minRadius, maxRadius) + transform.position;
            //Instantiate(sphere2, pos, sphere.transform.rotation);

            NavMeshHit hit;
            // そこから最も近いナビメッシュ上の点に赤い球を生成
            if (NavMesh.SamplePosition(pos, out hit, 20f, NavMesh.AllAreas))
            {
                Instantiate(sphere, hit.position, sphere.transform.rotation);
            }
        }
    }

    List<Transform> SortAgent(GameObject[] ag)
    {
        List<Transform> t = new List();

        foreach (GameObject agent in ag)
        {
            float d = Vector3.Distance(agent.transform.position, transform.position);

            if (d > maxAgentDist)
            {
                Destroy(agent);
            }
            else if (d >= minAgentDist)
            {
                t.Add(agent.transform);
            }     
        }

        return t;
    }

    IEnumerator DeployAgent()
    {

        List<Transform> transforms = SortAgent(GameObject.FindGameObjectsWithTag("Agent"));

        yield return null;

        // 周りに敵がいる時
        if (transforms.Count != 0)
        {

            // Vector3.forwardからの角度の配列
            float[] angles = new float[transforms.Count];

            for (int n = 0; n < transforms.Count; n++)
            {
                Vector3 agentPos = transforms[n].position;
                agentPos.y = transform.position.y;
                Vector3 agentDir = Vector3.Normalize(agentPos - transform.position);

                angles[n] = Mathf.Atan2(agentDir.x, agentDir.z); // Z軸との角度

            }

            Array.Sort(angles); // 小さい順に並べ替える


            yield return null;

            float preAngle = 0;


            for (int n = 0; n < angles.Length; n++)
            {
                float deltaAngle; // 角度の差

                // 最後
                if (n == angles.Length - 1)
                {

                    deltaAngle = angles[0] - angles[n] + 2f * Mathf.PI; // 前の角度との差
                                                                        // 一つしかない場合は 0 + 360度

                    InstantiateAgent(angles[n], deltaAngle);

                }
                // 2つ目以降
                // else if (n > 0)
                if (n > 0)
                {
                    deltaAngle = (angles[n] - preAngle); // 前の角度との差

                    InstantiateAgent(preAngle, deltaAngle);
                }

                preAngle = angles[n];
            }
        }
        // 周りに敵がいないとき
        else
        {
            InstantiateAgent(0, 360 * Mathf.Deg2Rad);
        }

    }
    
    // Update is called once per frame
    void Update()
    {
        sec += Time.deltaTime;

        text.text = Mathf.FloorToInt(sec).ToString();

        if (sec >= 3f)
        {            
            if (Vector3.Distance(transform.position, prePosition) >= deployInterval)
            {
                StartCoroutine("DeployAgent"); // エージェントを配置
                prePosition = transform.position; // プレイヤーの位置を保存
            }
            sec = 0f;
        }
    }
}

シーン上の敵を検索して、遠くにあるものは削除し、近くのものを除外したリストを使って、新しく敵を配置します。

1フレームで処理を完了する必要はないと思ったので、コルーチンにして適当に何フレームかに処理を分割しました。

Update()では、数秒おきに前の更新時のプレイヤーの位置と今の位置との距離を計算して、指定した値より大きければ上のコルーチンをスタートして、今の位置を保存します。

距離の計算を毎フレームやっても良いかもしれませんが、落下したりしてプレイヤーの移動速度が上がると、短い間隔でコルーチンが再スタートしてしまうので数秒おきに計算しています。

コメントを残す

メールアドレスが公開されることはありません。