150 lines
6.1 KiB
C#
150 lines
6.1 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
|
|
namespace Cielonos.MainGame.AttackArea
|
|
{
|
|
public static class AttackAreaDistributionHelper
|
|
{
|
|
/// <summary>
|
|
/// 在圆形范围内生成不重叠的随机位置列表,并自动吸附到 NavMesh 上。
|
|
/// </summary>
|
|
/// <param name="center">生成范围的中心点</param>
|
|
/// <param name="spawnRadius">允许生成的最大范围半径</param>
|
|
/// <param name="exclusionDistance">两个点之间的最小安全距离(防止重叠)</param>
|
|
/// <param name="count">需要生成的点位数量</param>
|
|
/// <param name="prePlacedPositions">已经预先放置好的点位(新生成的点位会避开这些点)</param>
|
|
/// <param name="maxAttemptsPerPoint">每个点位的最大尝试次数(防止死循环)</param>
|
|
/// <param name="navMeshSampleDistance">NavMesh 投影搜索的最大距离</param>
|
|
/// <returns>成功生成的随机点位列表</returns>
|
|
public static List<Vector3> GenerateNonOverlappingInCircle(
|
|
Vector3 center,
|
|
float spawnRadius,
|
|
float exclusionDistance,
|
|
int count,
|
|
List<Vector3> prePlacedPositions = null,
|
|
int maxAttemptsPerPoint = 30,
|
|
float navMeshSampleDistance = 10f)
|
|
{
|
|
List<Vector3> results = new List<Vector3>();
|
|
List<Vector3> occupiedPositions = new List<Vector3>();
|
|
if (prePlacedPositions != null)
|
|
{
|
|
occupiedPositions.AddRange(prePlacedPositions);
|
|
}
|
|
|
|
float minSqrDistance = exclusionDistance * exclusionDistance;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Vector3 bestPos = Vector3.zero;
|
|
bool found = false;
|
|
|
|
for (int attempt = 0; attempt < maxAttemptsPerPoint; attempt++)
|
|
{
|
|
Vector2 randomOffset = Random.insideUnitCircle * spawnRadius;
|
|
Vector3 candidatePos = center + new Vector3(randomOffset.x, 0f, randomOffset.y);
|
|
|
|
// NavMesh projection
|
|
if (NavMesh.SamplePosition(candidatePos, out NavMeshHit hit, navMeshSampleDistance, NavMesh.AllAreas))
|
|
{
|
|
candidatePos = hit.position;
|
|
}
|
|
|
|
if (IsValidPosition(candidatePos, occupiedPositions, minSqrDistance))
|
|
{
|
|
bestPos = candidatePos;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
results.Add(bestPos);
|
|
occupiedPositions.Add(bestPos);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在矩形范围内生成不重叠的随机位置列表,并自动吸附到 NavMesh 上。
|
|
/// </summary>
|
|
/// <param name="center">生成范围的中心点</param>
|
|
/// <param name="extents">矩形的半长宽 (x对应X轴, y对应Z轴)</param>
|
|
/// <param name="exclusionDistance">两个点之间的最小安全距离(防止重叠)</param>
|
|
/// <param name="count">需要生成的点位数量</param>
|
|
/// <param name="prePlacedPositions">已经预先放置好的点位(新生成的点位会避开这些点)</param>
|
|
/// <param name="maxAttemptsPerPoint">每个点位的最大尝试次数(防止死循环)</param>
|
|
/// <param name="navMeshSampleDistance">NavMesh 投影搜索的最大距离</param>
|
|
/// <returns>成功生成的随机点位列表</returns>
|
|
public static List<Vector3> GenerateNonOverlappingInRect(
|
|
Vector3 center,
|
|
Vector2 extents,
|
|
float exclusionDistance,
|
|
int count,
|
|
List<Vector3> prePlacedPositions = null,
|
|
int maxAttemptsPerPoint = 30,
|
|
float navMeshSampleDistance = 10f)
|
|
{
|
|
List<Vector3> results = new List<Vector3>();
|
|
List<Vector3> occupiedPositions = new List<Vector3>();
|
|
if (prePlacedPositions != null)
|
|
{
|
|
occupiedPositions.AddRange(prePlacedPositions);
|
|
}
|
|
|
|
float minSqrDistance = exclusionDistance * exclusionDistance;
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Vector3 bestPos = Vector3.zero;
|
|
bool found = false;
|
|
|
|
for (int attempt = 0; attempt < maxAttemptsPerPoint; attempt++)
|
|
{
|
|
float randomX = Random.Range(-extents.x, extents.x);
|
|
float randomZ = Random.Range(-extents.y, extents.y);
|
|
Vector3 candidatePos = center + new Vector3(randomX, 0f, randomZ);
|
|
|
|
// NavMesh projection
|
|
if (NavMesh.SamplePosition(candidatePos, out NavMeshHit hit, navMeshSampleDistance, NavMesh.AllAreas))
|
|
{
|
|
candidatePos = hit.position;
|
|
}
|
|
|
|
if (IsValidPosition(candidatePos, occupiedPositions, minSqrDistance))
|
|
{
|
|
bestPos = candidatePos;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
results.Add(bestPos);
|
|
occupiedPositions.Add(bestPos);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
private static bool IsValidPosition(Vector3 candidatePos, List<Vector3> occupiedPositions, float minSqrDistance)
|
|
{
|
|
foreach (var pos in occupiedPositions)
|
|
{
|
|
// 为了防止在楼上楼下的重叠,我们使用 3D 距离,但在大多数平面场景下也等同于 2D 距离
|
|
if ((candidatePos - pos).sqrMagnitude < minSqrDistance)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|