四叉树在物体超200且分布不均时将碰撞检测从O(n²)降至O(n log n),但均匀分布或总数<50时反更慢;其核心是空间剪枝,需轻量的动态insert/query/update、合理分裂阈值(6–8)与深度限制(6–8层),并用epsilon处理浮点精度。

四叉树在2D碰撞检测中到底省了多少计算?
直接说结论:当物体数量超过 200 且分布不均匀(比如大量集中在屏幕一角)时,用 QuadTree 通常能把平均碰撞对检查数从 O(n²) 压到 O(n log n) 量级;但如果所有物体均匀铺满整个区域、或总数始终低于 50,它反而因节点分裂/遍历开销而更慢。
关键不是“用了就快”,而是“它把“谁可能和谁撞”这个判断提前做了空间剪枝”。你得让插入、查询、更新三个操作都轻量,否则树本身就成了瓶颈。
insert() 和 query() 必须支持动态移动物体
很多教程实现的 QuadTree 只能静态插入一次,但游戏或物理模拟中物体每帧都在动——如果每帧清空重建,性能比暴力还差。
-
insert()应返回一个句柄(如std::shared_ptr或自定义ObjectId),后续通过该句柄调用updatePosition(x, y)触发上浮/下沉重分配 -
query()接收的是包围盒(Rect或AABB),不是单点;因为碰撞检测本质是两个 AABB 是否相交,不是“点是否在区域内” - 节点分裂阈值别硬写死为 4;实际建议设为 6–8,太小导致频繁分裂,太大失去剪枝效果
- 深度限制建议设为 6–8 层;超过后不再分裂,直接存入叶子节点——避免极小物体(如子弹)导致深层递归崩溃
常见 crash 来源:浮点精度 + 边界条件
错误现象包括:物体突然“消失”、同一帧重复报告碰撞、segmentation fault 在 query() 递归中。
- 用
double存坐标?没必要,float足够,但所有比较必须加 epsilon:if (abs(a - b) ,尤其在判断点是否在边界上时 - 矩形边界必须显式定义为
[x, x + w), [y, y + h)(左闭右开),否则相邻节点可能漏查或重复包含同一物体 - 物体 AABB 宽高为 0 时(如未初始化的精灵),插入前必须 clamp:
w = fmaxf(w, 1e-4f); h = fmaxf(h, 1e-4f); - 不要在
query()递归里修改树结构(如删除刚碰撞的物体);先收集所有待查物体 ID,退出递归后再批量处理
C++ 实现时别踩 std::vector 的隐式拷贝坑
典型错误:节点类里用 std::vector,每次 split() 就把父节点 vector 拷贝四份——O(n) 拷贝成本瞬间吃掉优化收益。
- 改用
std::vector<:shared_ptr>>或裸指针 + 外部容器管理(如std::vector配合std::vector存索引) - 叶子节点对象列表建议 reserve(8),避免多次 realloc;非叶子节点的 children 指针数组固定大小为 4,用
std::array更高效 - 若用
std::unique_ptr管理子节点,确保 move 构造函数正确,别意外触发拷贝
最易被忽略的一点:四叉树只负责“粗筛”,它返回的候选对仍需用精确几何检测(如分离轴定理 SAT 或圆形距离平方比较)确认是否真碰撞。别在 query() 里塞业务逻辑,那会让树失去复用性,也掩盖真实热点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











