目录
前言
碰撞检测是一个非常经典的问题。虽然现在我们都有物理引擎提供的便捷接口,很多时候不需要我们手动实现,但是了解其中的算法思想有助于拓展我们的思维。而且很多问题其实都可以转化为碰撞检测问题,如:视锥体裁剪、LOD(视野范围与显示对象的碰撞)、RTS游戏中单位自动攻击警戒范围内的敌方单位(警戒范围与敌方单位的碰撞)、光线追踪等。 本次分享的不是碰撞检测中的检测算法(分离轴、GJK等),而是用于过滤检测对象的空间划分算法。我将通过实现一个碰撞检测的小demo来介绍碰撞检测中如何使用空间划分做优化以及他们的实现原理。
一、Demo说明
在一个100*100的空间内,随机生成2000个1*1的小方块,小方块生成后会随机赋予一个初速度,小方块将沿速度方向匀速运动,碰到空间边界会反弹。小方块之间不会反弹。现在需要检查方块是否与其他方块发生了碰撞(相交),是的话将方块标记为红色,否则为白色。 每种方法的测试中都将列出以下数据做为对比:帧率(FPS)、维护空间数据结构耗时(rebuild time)、碰撞检测耗时(check time)、碰撞检测函数调用次数(intersectsCheckCount)。
使用trigger
使用Physics.OverlapBoxNonAlloc
二、暴力法
如果我们直接对所有的方块两两检测是否发生碰撞,那么算法的时间复杂度将是N平方。在测试中的2000个方块已经卡成PPT,不足1帧。
暴力法效果图
三、网格划分
可以发现在暴力法中做了太多多余的检查,如果能提前过滤掉一些明显不可能发生碰撞的方块就好了。 思路:将空间按网格分块,只对有可能发生碰撞的网格内的方块进行碰撞检测。具体做法就开启一个网格的数组,然后根据方块的坐标计算出应该属于哪些格子,最后只需要对每个格子内的方块进行碰撞检测即可。
实测
下面是将使用32*32的网格来划分空间,这里可以通过对方块的左下角和右上角坐标取模快速得到方块属于哪些网格。经过网格划分之后性能得到了大幅提升。
网格划分效果图
分析
网格划分在大部分时候都是简单又有效的。但是缺点也很明显,如果我们的空间再大点,方块分布也不是这么均匀的话。我们的网格大小就不好定了。网格太大的话碰撞检测的候选项就会增多,降低优化效果。网格太小的话就需要开辟更多的网格节点,而且可能很多节点都不会包含方块造成内存的浪费。下面是将空间扩大到1000*1000,方块初始位置设为左下角后的测试。