松散四叉树
松散四叉树的核心思想
每个子节点的实际大小是标准四叉树的2倍,但逻辑上的划分点不变。
这样,对象即使稍微移动,仍然可能停留在同一个节点中,不需要频繁删除/重新插入。
当查询时,虽然子节点范围更大,但整体查询速度仍然优于普通四叉树。
示意图(普通 vs. 松散四叉树)
普通四叉树:
+----+----+
| 1 | 2 |
+----+----+
| 3 | 4 |
+----+----+
(对象跨越 2,3 节点,需要存储在多个子节点)
松散四叉树(每个子区域放大2倍):
+----+----+
| 1 | 2 |
+----+----+
| 3 | 4 |
+----+----+
(对象跨越区域,但仍然存储在 1 个子节点)
优势
✅ 适合动态对象:
允许对象在一定范围内移动,而无需频繁调整四叉树结构。
比普通四叉树更适合游戏场景(如怪物 AI、子弹碰撞等)。
✅ 查询更高效:
避免对象存储在多个子节点,减少不必要的查询。
✅ 插入/删除性能更好:
由于松散范围,减少了对象需要重新插入的次数。
`
class LooseQuadtree {
class Node {
float x, y, size;
List
Node[] children;
Node(float x, float y, float size) {
this.x = x;
this.y = y;
this.size = size * 2; // 扩大节点范围(松散)
this.children = null;
}
boolean contains(GameObject obj) {
return obj.x >= x - size / 2 && obj.x <= x + size / 2 &&
obj.y >= y - size / 2 && obj.y <= y + size / 2;
}
}
private Node root;
public LooseQuadtree(float x, float y, float size) {
root = new Node(x, y, size);
}
public void insert(GameObject obj) {
insert(root, obj);
}
private void insert(Node node, GameObject obj) {
if (!node.contains(obj)) return; // 物体不在该节点范围内
if (node.children == null) { // 叶子节点
node.objects.add(obj);
return;
}
// 递归插入到子节点
for (Node child : node.children) {
insert(child, obj);
}
}
public List<GameObject> queryRange(float qx, float qy, float qsize) {
return queryRange(root, qx, qy, qsize);
}
private List<GameObject> queryRange(Node node, float qx, float qy, float qsize) {
List<GameObject> results = new ArrayList<>();
if (node == null) return results;
if (!intersects(node, qx, qy, qsize)) return results;
results.addAll(node.objects);
if (node.children != null) {
for (Node child : node.children) {
results.addAll(queryRange(child, qx, qy, qsize));
}
}
return results;
}
private boolean intersects(Node node, float qx, float qy, float qsize) {
return !(node.x - node.size / 2 > qx + qsize / 2 ||
node.x + node.size / 2 < qx - qsize / 2 ||
node.y - node.size / 2 > qy + qsize / 2 ||
node.y + node.size / 2 < qy - qsize / 2);
}
}
class GameObject {
float x, y;
}
`