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