常见的2D碰撞检测全解析:方法、优化与实战建议

常见的2D碰撞检测

在2D游戏开发、图形界面交互及物理模拟中,2D碰撞检测是核心功能之一。它决定了物体是否发生接触、碰撞后的响应方式,直接影响用户体验的真实性与流畅性。本文将从基础方法到高级优化,系统梳理常见的2D碰撞检测技术,并提供可落地的实战建议。

一、基础几何形状的碰撞检测

1. 轴对齐包围盒(AABB, Axis-Aligned Bounding Box)

原理:AABB是最简单的碰撞检测方法,通过比较两个矩形在X轴和Y轴上的投影是否重叠来判断碰撞。
数学表达
设矩形A的坐标为$(x_1, y_1, x_2, y_2)$(左下角与右上角),矩形B为$(x_3, y_3, x_4, y_4)$,则碰撞条件为:
x1<x4 &amp;&amp; x3<x2 &amp;&amp; y1<y4 &amp;&amp; y3<y2x_1 < x_4 \ \&amp;\&amp; \ x_3 < x_2 \ \&amp;\&amp; \ y_1 < y_4 \ \&amp;\&amp; \ y_3 < y_2
优点:计算量小,适合动态物体多的场景(如角色移动)。
缺点:无法处理旋转矩形,需结合其他方法(如OBB)。
代码示例(JavaScript):

  1. function checkAABBCollision(rectA, rectB) {
  2. return rectA.x < rectB.x + rectB.width &&
  3. rectA.x + rectA.width > rectB.x &&
  4. rectA.y < rectB.y + rectB.height &&
  5. rectA.y + rectA.height > rectB.y;
  6. }

2. 圆形碰撞检测

原理:通过比较两圆心距离与半径之和判断碰撞。
数学表达
设圆A的圆心为$(x_1, y_1)$,半径为$r_1$;圆B为$(x_2, y_2)$,半径为$r_2$,则碰撞条件为:
(x2x1)2+(y2y1)2r1+r2\sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} \leq r_1 + r_2
优化:避免开方运算,改用平方比较:
(x2x1)2+(y2y1)2(r1+r2)2(x_2 - x_1)^2 + (y_2 - y_1)^2 \leq (r_1 + r_2)^2
适用场景:粒子系统、弹道轨迹等。
代码示例(Python):

  1. import math
  2. def checkCircleCollision(circleA, circleB):
  3. dx = circleA['x'] - circleB['x']
  4. dy = circleA['y'] - circleB['y']
  5. distance_sq = dx*dx + dy*dy
  6. return distance_sq <= (circleA['radius'] + circleB['radius'])**2

二、多边形与复杂形状的碰撞检测

1. 分离轴定理(SAT, Separating Axis Theorem)

原理:若两个凸多边形在任意一条轴上的投影不重叠,则它们不相交。
步骤

  1. 遍历两个多边形的每条边,计算其法线作为分离轴。
  2. 将两个多边形投影到分离轴上,检查投影区间是否重叠。
  3. 若所有轴的投影均重叠,则发生碰撞。
    优点:可处理任意方向的凸多边形。
    缺点:对凹多边形需先分解为凸多边形。
    代码示例(伪代码):
    ```python
    def projectPolygon(polygon, axis):
    min_proj = max_proj = dot(polygon[0], axis)
    for vertex in polygon[1:]:
    1. proj = dot(vertex, axis)
    2. min_proj = min(min_proj, proj)
    3. max_proj = max(max_proj, proj)

    return (min_proj, max_proj)

def checkSATCollision(polyA, polyB):
axes = getNormals(polyA) + getNormals(polyB) # 获取所有分离轴
for axis in axes:
projA = projectPolygon(polyA, axis)
projB = projectPolygon(polyB, axis)
if not overlap(projA, projB):
return False
return True
```

2. 像素级碰撞检测(Per-Pixel Collision)

原理:通过比较两个精灵的透明像素区域判断碰撞。
实现步骤

  1. 渲染两个物体到离屏缓冲区,禁用颜色写入,仅启用深度测试。
  2. 读取像素数据,检查重叠区域是否有非透明像素。
    适用场景:精细碰撞需求(如平台游戏中的角色与地形)。
    优化建议
  • 结合AABB预检测,减少像素级检测次数。
  • 使用掩码纹理(Mask Texture)加速判断。

三、性能优化策略

1. 空间分区技术

  • 四叉树(Quadtree):将2D空间递归划分为四个象限,仅检测同一分区内的物体。
  • 网格分区(Grid):将场景划分为固定大小的网格,每个网格维护物体列表。
    适用场景:大规模动态物体(如子弹、粒子)。

2. 粗检测与细检测结合

  1. 粗检测:使用AABB或圆形快速排除不可能碰撞的物体。
  2. 细检测:对可能碰撞的物体使用SAT或像素级检测。
    案例:在《愤怒的小鸟》中,先用圆形检测鸟类与猪的碰撞,再用像素级检测精确碰撞点。

3. 惰性检测(Lazy Evaluation)

  • 仅在物体位置或形状发生变化时更新碰撞信息。
  • 适用于静态场景或低频更新物体。

四、实战建议

  1. 选择合适的方法

    • 简单矩形:优先用AABB。
    • 圆形物体:使用圆形检测。
    • 复杂形状:SAT或分解为凸多边形。
  2. 避免过度检测

    • 对远离屏幕的物体禁用碰撞检测。
    • 使用“睡眠”机制(Sleeping)减少静止物体的计算。
  3. 调试工具

    • 可视化碰撞边界(如用红色框显示AABB)。
    • 记录碰撞事件日志,便于问题排查。
  4. 物理引擎集成

    • 对复杂需求,可集成Box2D、Chipmunk等物理引擎。
    • 注意引擎的坐标系与项目坐标系的转换。

五、总结

2D碰撞检测是游戏与交互应用的核心技术,其选择需平衡精度与性能。从基础的AABB到高级的SAT,再到像素级检测,开发者应根据场景需求灵活组合。通过空间分区、粗细检测结合等优化策略,可显著提升性能。最终,实战中的调试工具与物理引擎集成能进一步降低开发成本。掌握这些方法后,你将能高效实现从简单弹球到复杂平台游戏的物理交互。