using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if Unity_Runtime
namespace XericLibrary.Runtime.Type.SpatialAlgorithm
{
///
/// 灵活的四叉树实现,支持多种坐标类型
///
public class QuadTree
{
#region 字段属性
private int maxObjectsPerNode = 5;
private int maxDepth = 4;
private Node root;
private Func getRectFunc; // 获取对象边界的委托
///
/// 获取此四叉树的范围
///
public Rect RootRect => root != null ? root.boundary : Rect.zero;
public int Count { get; private set; }
///
/// 清空四叉树,保留边界和配置以便重用
///
public void Clear()
{
if (root != null)
{
root.ClearAll();
}
Count = 0;
}
///
/// 重建四叉树,使用相同边界和配置
///
public void Rebuild(IEnumerable items)
{
Clear();
foreach (var item in items)
{
Insert(item);
}
}
///
/// 获取四叉树中所有元素
///
public void GetAllItems(HashSet result)
{
if (result == null)
return;
root?.CollectAllItems(result);
}
#endregion
#region 构造函数
///
/// 初始化四叉树
///
/// 四叉树边界
/// 获取对象边界的函数
public QuadTree(Rect boundary, Func getRectFunc, int maxObjectsPerNode = 5, int maxDepth = 4)
{
this.maxObjectsPerNode = Math.Max(1, maxObjectsPerNode);
this.maxDepth = Math.Max(1, maxDepth);
this.root = new Node(boundary, 0, getRectFunc, this);
this.getRectFunc = getRectFunc ?? throw new ArgumentNullException(nameof(getRectFunc));
}
#endregion
#region 公共方法
///
/// 插入元素
///
public bool Insert(T item)
{
return root.Insert(item);
}
///
/// 查找范围内的元素
///
/// 查找范围
/// 建议使用数据容器对象池控制,避免频繁GC,默认空将自动创建新的数据容器
///
public HashSet Retrieve(Rect area, HashSet result = null)
{
return root.Retrieve(area, result);
}
///
/// 查找最近的元素
///
public T FindNearest(Vector2 target, float maxDistance = float.MaxValue)
{
T nearest = default;
float minDistanceSquared = maxDistance * maxDistance;
root.FindNearest(target, ref nearest, ref minDistanceSquared);
return nearest;
}
///
/// 调试绘制网格
///
public void DrawGrid()
{
root.DrawGrid(Color.white);
}
#endregion
#region 节点类
private class Node : IEnumerable
{
internal Rect boundary;
internal HashSet objects = new HashSet();
internal Node[] children;
internal readonly int depth;
internal readonly Func getRectFunc;
internal readonly QuadTree parent;
public Node(Rect boundary, int depth, Func getRectFunc, QuadTree parent)
{
this.boundary = boundary;
this.depth = depth;
this.getRectFunc = getRectFunc;
this.parent = parent;
this.children = null;
}
///
/// 插入元素
///
public bool Insert(T item)
{
Rect itemRect = getRectFunc(item);
// 如果元素与当前节点不重叠,无法插入
if (!IsOverlapping(boundary, itemRect))
return false;
// 如果是叶子节点且未达容量上限
if (objects != null && (objects.Count < parent.maxObjectsPerNode || depth >= parent.maxDepth))
{
objects.Add(item);
parent.Count++;
return true;
}
// 分裂节点
if (children == null)
Split();
// 插入到子节点
bool inserted = false;
foreach (var child in children)
{
if (child.Insert(item))
inserted = true;
}
return inserted;
}
///
/// 分裂为四个子节点
///
private void Split()
{
float halfWidth = boundary.width / 2;
float halfHeight = boundary.height / 2;
float midX = boundary.x + halfWidth;
float midY = boundary.y + halfHeight;
children = new Node[4];
children[0] = new Node(new Rect(boundary.x, boundary.y, halfWidth, halfHeight), depth + 1, getRectFunc, parent); // 左上
children[1] = new Node(new Rect(midX, boundary.y, halfWidth, halfHeight), depth + 1, getRectFunc, parent); // 右上
children[2] = new Node(new Rect(boundary.x, midY, halfWidth, halfHeight), depth + 1, getRectFunc, parent); // 左下
children[3] = new Node(new Rect(midX, midY, halfWidth, halfHeight), depth + 1, getRectFunc, parent); // 右下
// 移动当前节点的对象到子节点
var tempObjects = new List(objects);
objects.Clear();
objects = null; // 标记为非叶子节点
foreach (var obj in tempObjects)
{
Insert(obj);
}
}
///
/// 查找最近的元素
///
public void FindNearest(Vector2 target, ref T nearest, ref float minDistanceSquared)
{
// 如果当前节点距离大于已知最小距离,直接返回
float distanceToBoundary = GetDistanceToRectSquared(target, boundary);
if (distanceToBoundary >= minDistanceSquared)
return;
// 检查当前节点中的对象
if (objects != null)
{
foreach (var obj in objects)
{
Rect objRect = getRectFunc(obj);
float distanceSquared = GetDistanceToRectSquared(target, objRect);
if (distanceSquared < minDistanceSquared)
{
minDistanceSquared = distanceSquared;
nearest = obj;
}
}
}
// 递归检查子节点
if (children != null)
{
// 按距离排序子节点,优先检查更近的
var childDistances = new List>();
foreach (var child in children)
{
float dist = child.GetDistanceToRectSquared(target, child.boundary);
childDistances.Add(Tuple.Create(child, dist));
}
// 按距离升序排序
childDistances.Sort((a, b) => a.Item2.CompareTo(b.Item2));
foreach (var tuple in childDistances)
{
if (tuple.Item2 < minDistanceSquared)
{
tuple.Item1.FindNearest(target, ref nearest, ref minDistanceSquared);
}
}
}
}
///
/// 查找范围内的元素
///
public HashSet Retrieve(Rect area, HashSet result)
{
if (result == null)
result = new HashSet();
// 如果与当前节点不重叠,返回空集
if (!IsOverlapping(boundary, area))
return result;
// 检查当前节点中的对象
if (objects != null)
{
foreach (var obj in objects)
{
if (IsOverlapping(getRectFunc(obj), area))
{
result.Add(obj);
}
}
}
// 递归查询子节点
if (children != null)
{
foreach (var child in children)
{
var childResults = child.Retrieve(area, result);
foreach (var item in childResults)
{
result.Add(item);
}
}
}
return result;
}
///
/// 绘制节点网格
///
public void DrawGrid(Color color)
{
Debug.DrawLine(new Vector2(boundary.x, boundary.y), new Vector2(boundary.xMax, boundary.y), color);
Debug.DrawLine(new Vector2(boundary.x, boundary.y), new Vector2(boundary.x, boundary.yMax), color);
Debug.DrawLine(new Vector2(boundary.xMax, boundary.y), new Vector2(boundary.xMax, boundary.yMax), color);
Debug.DrawLine(new Vector2(boundary.x, boundary.yMax), new Vector2(boundary.xMax, boundary.yMax), color);
if (children != null)
{
foreach (var child in children)
{
child.DrawGrid(color);
}
}
}
#region 辅助方法
///
/// 判断两个矩形是否重叠
///
private bool IsOverlapping(Rect a, Rect b)
{
return a.x < b.xMax && a.xMax > b.x &&
a.y < b.yMax && a.yMax > b.y;
}
///
/// 计算点到矩形的最小距离平方
///
private float GetDistanceToRectSquared(Vector2 point, Rect rect)
{
float dx = Mathf.Max(rect.x - point.x, point.x - rect.xMax, 0);
float dy = Mathf.Max(rect.y - point.y, point.y - rect.yMax, 0);
return dx * dx + dy * dy;
}
///
/// 递归清空所有节点
///
public void ClearAll()
{
if (objects != null)
{
objects.Clear();
}
if (children != null)
{
for (int i = 0; i < children.Length; i++)
{
children[i]?.ClearAll();
}
children = null;
}
objects = new HashSet();
}
///
/// 递归收集所有元素
///
public void CollectAllItems(HashSet result)
{
if (objects != null)
{
foreach (var obj in objects)
{
result.Add(obj);
}
return;
}
if (children != null)
{
for (int i = 0; i < children.Length; i++)
{
children[i]?.CollectAllItems(result);
}
}
}
public IEnumerator GetEnumerator()
{
if (objects != null)
yield return this;
else
foreach (var child in children)
foreach (var node in child)
yield return node;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
#endregion
}
///
/// 四叉树扩展方法,提供不同类型的矩形转换
///
public static class QuadTreeExtensions
{
// Vector2 扩展 - 视为点
public static Rect ToQuadTreeRect(this Vector2 vector)
{
return new Rect(vector, Vector2.zero);
}
// Vector3 扩展 - 使用X和Y坐标,视为点
public static Rect ToQuadTreeRect(this Vector3 vector)
{
return new Rect(vector.x, vector.y, 0, 0);
}
// Rect 扩展 - 直接使用自身作为边界
public static Rect ToQuadTreeRect(this Rect rect)
{
return rect;
}
// RectTransform 扩展 - 使用Rect范围
public static Rect ToQuadTreeRect(this RectTransform transform)
{
return transform.rect;
}
// Transform 扩展 - 使用位置的X和Y坐标,视为点
public static Rect ToQuadTreeRect(this Transform transform)
{
Vector3 pos = transform.position;
return new Rect(pos.x, pos.y, 0, 0);
}
// 简化四叉树创建的扩展方法
public static QuadTree CreateQuadTree(this IEnumerable collection, Rect boundary, Func getRectFunc)
{
var quadTree = new QuadTree(boundary, getRectFunc);
foreach (var item in collection)
{
quadTree.Insert(item);
}
return quadTree;
}
}
}
#endif