移動するプレイヤーを囲うように敵を配置するときに、すでに敵がいる方向へは新たに敵を配置しないようにします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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; // 最小値
// Start is called before the first frame update
void Start()
{
}
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);
}
}
}
// Update is called once per frame
void Update()
{
// 今いる敵の隙間に敵を配置
if (Input.GetKeyDown(KeyCode.T))
{
GameObject[] agents = GameObject.FindGameObjectsWithTag("Agent");
// 周りに敵がいる時
if (agents.Length != 0)
{
// Vector3.forwardからの角度の配列
float[] angles = new float[agents.Length];
for (int n = 0; n < agents.Length; n++)
{
Vector3 agentPos = agents[n].transform.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); // 小さい順に並べ替える
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);
}
}
}
}
まずタグでシーン上の敵を探して配列に入れます。敵がいない時は敵を出現させる範囲を制限しません。プレイヤーから近すぎたり遠すぎる敵は除外したほうがいいかもしれません。
敵がいたら、プレイヤーと敵の高さを揃えたときの、プレイヤーから各敵への方向とZ軸との角度を配列に入れていきます。
それを小さい方から並べ替えて、隣り合う2つの角度を順に見ていって、角度の差を求めます。それと指定した角度を比べて、その2つ敵の間に新たに敵を配置するか、何体配置するかを決めます。
例えば、指定した角度が40度で、差が40度の場合敵を配置しませんが、80度なら、差を指定した角度で割った整数部分は2であり、ちょうど真ん中に1体だけ敵を追加したいです。追加する敵の角度は、小さい方の角度 + 差 * 1/2 です。
差が120度の場合、割った整数部分は3であり、間に均等に2体敵を配置したいので、追加する敵の角度は、小さい方の角度 + 差 * 1/3と、小さい方の角度 + 差 * 2/3です。
このことから、追加する敵の角度は、(小さい方の角度 )+(隣り合う角度の差)*(この隙間に何番目の敵を追加するか) /(割った整数部分)であると思われます。隣り合う敵の隙間に(割った整数部分)-1体(>=0)の敵を新しく生成します。
追加する敵の角度がわかれば、それをクォータニオンにして、基準のZ軸方向をかければ、追加する敵の方向がわかるので、プレイヤーの位置から、その方向へ指定した範囲でランダムに引き伸ばした地点に敵を生成します。
これでプレイヤーからみて敵と敵の間に、角度に応じた数のオブジェクトを配置できますが、ナビメッシュ上に配置されないと困るので、前の記事の方法で、この地点から最も近いナビメッシュ上の点に敵を配置します。
赤い球が青い球の近くのナビメッシュ上にあるのがわかります。