前の記事のプレイヤーを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()では、数秒おきに前の更新時のプレイヤーの位置と今の位置との距離を計算して、指定した値より大きければ上のコルーチンをスタートして、今の位置を保存します。
距離の計算を毎フレームやっても良いかもしれませんが、落下したりしてプレイヤーの移動速度が上がると、短い間隔でコルーチンが再スタートしてしまうので数秒おきに計算しています。