在 Unity 开发中,最近邻问题是一个常见的需求场景。例如,在游戏中的寻路系统、物体之间的交互检测、资源分配等场景中,都需要快速准确地找到某个点或物体的最近邻。然而,传统的暴力遍历方法在处理这类问题时,往往会暴露出严重的性能瓶颈,而 KD 树作为一种高效的数据结构,能够显著优化最近邻问题的求解效率。
开发的需求场景:
需要在一个静态的地图中,寻找最近的对象,然后删除它,接下来寻找下一个最近的对象。
KD 树的基本原理
KD 树是一种二叉树结构,它通过递归地将数据空间划分为两个子空间,从而实现对数据点的有序组织。参考视频
《游戏中的空间划分》四叉树、KD树如何优化游戏性能
KD Tree 的缺陷
在需要频繁插入或删除数据点的场景中,KD树的维护成本较高。每次插入或删除操作都可能需要重新调整树的结构,以保持其平衡性。这种操作不仅耗时,还可能导致内存占用增加。
SO在Cold KD Tree中,需要被忽略的节点会被标记为Deleted,进行一种冷处理。
示例仓库
GitHub
核心代码
private int nearest(Vector3 point, Point p, int depth)
{
if (p == null)
return -1;
int best = -1;
if (!p.deleted)
{
best = p.mid; // 当前节点有效时,初始化最佳候选
}
int axis = p.axis;
// 计算分割距离
float distToSplit = point[axis] - points[p.mid][axis];
// 优先搜索更近的分支
Point firstChild = distToSplit < 0 ? p.smaller : p.larger;
Point secondChild = distToSplit < 0 ? p.larger : p.smaller;
// 递归搜索第一子树
int candidate = nearest(point, firstChild, depth + 1);
best = closer(point, best, candidate);
// 检查是否需要搜索第二子树
float bestDist = sqDist(point, best);
if (secondChild != null && (best == -1 || Mathf.Abs(distToSplit) < Mathf.Sqrt(bestDist)))
{
candidate = nearest(point, secondChild, depth + 1);
best = closer(point, best, candidate);
}
return best;
}