精准碰撞:Canvas小游戏核心检测技术解析

引言

在Canvas小游戏开发中,碰撞检测是构建游戏逻辑的核心环节。无论是角色移动、子弹射击还是物体交互,精准的碰撞判断直接影响游戏体验。本文将系统梳理Canvas小游戏开发中常用的碰撞检测方法,从基础几何检测到高级像素级检测,结合代码示例与优化技巧,为开发者提供完整的解决方案。

一、矩形碰撞检测:基础中的基础

矩形碰撞检测是游戏开发中最基础的检测方式,适用于大多数规则形状的物体碰撞判断。其核心原理是通过比较两个矩形的边界坐标是否重叠来实现。

1.1 轴对齐矩形检测(AABB)

轴对齐矩形检测(Axis-Aligned Bounding Box)是最简单的矩形碰撞检测方法。其实现逻辑如下:

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

适用场景:角色与地面、墙壁等规则物体的碰撞检测。
优化建议:在移动帧率较低的游戏中,可结合速度预测提前判断碰撞。

1.2 动态矩形检测

当物体发生旋转时,需将矩形坐标转换至同一坐标系后再检测:

  1. function rotatePoint(x, y, angle, originX, originY) {
  2. const rad = angle * Math.PI / 180;
  3. const cos = Math.cos(rad);
  4. const sin = Math.sin(rad);
  5. return {
  6. x: (x - originX) * cos - (y - originY) * sin + originX,
  7. y: (x - originX) * sin + (y - originY) * cos + originY
  8. };
  9. }

技术要点:需先计算旋转后的顶点坐标,再转换为AABB矩形进行检测。

二、圆形碰撞检测:精准的球体交互

圆形碰撞检测通过计算两圆心距离与半径和的关系实现,适用于子弹、粒子等圆形物体的碰撞判断。

2.1 基础圆形检测

  1. function checkCircleCollision(circle1, circle2) {
  2. const dx = circle1.x - circle2.x;
  3. const dy = circle1.y - circle2.y;
  4. const distance = Math.sqrt(dx * dx + dy * dy);
  5. return distance < circle1.radius + circle2.radius;
  6. }

性能优化:使用距离平方比较避免开方运算:

  1. const distanceSquared = dx * dx + dy * dy;
  2. const radiusSumSquared = (circle1.radius + circle2.radius) ** 2;
  3. return distanceSquared < radiusSumSquared;

2.2 圆形与矩形混合检测

结合矩形检测与圆形检测的优化方法:

  1. function checkCircleRectCollision(circle, rect) {
  2. const closestX = Math.max(rect.x, Math.min(circle.x, rect.x + rect.width));
  3. const closestY = Math.max(rect.y, Math.min(circle.y, rect.y + rect.height));
  4. const distanceX = circle.x - closestX;
  5. const distanceY = circle.y - closestY;
  6. const distanceSquared = distanceX * distanceX + distanceY * distanceY;
  7. return distanceSquared < circle.radius * circle.radius;
  8. }

三、像素级碰撞检测:极致精准的实现

对于不规则形状物体,像素级检测能提供最精确的碰撞判断。Canvas提供了getImageDataAPI实现该功能。

3.1 基础实现方案

  1. function checkPixelCollision(ctx1, rect1, ctx2, rect2) {
  2. // 创建离屏Canvas
  3. const offscreenCanvas = document.createElement('canvas');
  4. const offscreenCtx = offscreenCanvas.getContext('2d');
  5. // 绘制第一个物体
  6. offscreenCtx.clearRect(0, 0, rect1.width, rect1.height);
  7. offscreenCtx.drawImage(ctx1.canvas, rect1.x, rect1.y, rect1.width, rect1.height, 0, 0, rect1.width, rect1.height);
  8. const data1 = offscreenCtx.getImageData(0, 0, rect1.width, rect1.height).data;
  9. // 绘制第二个物体(需坐标转换)
  10. // ...(实现略)
  11. // 像素比较逻辑
  12. for (let y = 0; y < Math.min(rect1.height, rect2.height); y++) {
  13. for (let x = 0; x < Math.min(rect1.width, rect2.width); x++) {
  14. const alpha1 = data1[(y * rect1.width + x) * 4 + 3];
  15. const alpha2 = data2[(y * rect2.width + x) * 4 + 3];
  16. if (alpha1 > 0 && alpha2 > 0) return true;
  17. }
  18. }
  19. return false;
  20. }

性能瓶颈:该方案在移动端性能较差,建议仅在关键交互时使用。

3.2 优化策略

  1. 区域采样:将检测区域划分为网格,仅对可能碰撞的网格进行像素检测
  2. 颜色标记法:使用特定颜色通道存储碰撞信息,减少数据量
  3. 缓存机制:对静态物体预先计算碰撞掩模

四、分离轴定理(SAT):复杂多边形的终极方案

对于任意凸多边形,分离轴定理提供最通用的解决方案。其核心思想是:若两个凸多边形在任意轴上的投影不重叠,则它们不相交。

4.1 算法实现

  1. function checkSATCollision(polygon1, polygon2) {
  2. const polygons = [polygon1, polygon2];
  3. for (let i = 0; i < polygons.length; i++) {
  4. const polygon = polygons[i];
  5. const otherPolygon = polygons[(i + 1) % 2];
  6. for (let j = 0; j < polygon.vertices.length; j++) {
  7. const nextIndex = (j + 1) % polygon.vertices.length;
  8. const edge = {
  9. x: polygon.vertices[nextIndex].x - polygon.vertices[j].x,
  10. y: polygon.vertices[nextIndex].y - polygon.vertices[j].y
  11. };
  12. const normal = { x: -edge.y, y: edge.x };
  13. const min1 = projectPolygon(polygon, normal);
  14. const min2 = projectPolygon(otherPolygon, normal);
  15. if (min1.max < min2.min || min2.max < min1.min) {
  16. return false;
  17. }
  18. }
  19. }
  20. return true;
  21. }
  22. function projectPolygon(polygon, axis) {
  23. let min = Infinity;
  24. let max = -Infinity;
  25. for (const vertex of polygon.vertices) {
  26. const projection = vertex.x * axis.x + vertex.y * axis.y;
  27. min = Math.min(min, projection);
  28. max = Math.max(max, projection);
  29. }
  30. return { min, max };
  31. }

实现要点

  1. 需预先计算多边形的边法向量
  2. 投影计算需考虑浮点数精度问题
  3. 适用于凸多边形,凹多边形需先分解为凸多边形

五、性能优化实战建议

  1. 分层检测:先进行粗略检测(如AABB),再对可能碰撞的对象进行精确检测
  2. 空间分区:使用四叉树或网格分区减少检测次数
  3. 休眠机制:对静止物体暂停碰撞检测
  4. Web Workers:将复杂检测计算移至后台线程

六、实战案例解析

以经典《打砖块》游戏为例:

  1. 球体与砖块检测:使用圆形与矩形混合检测
  2. 砖块破碎效果:碰撞后将砖块分解为多个小矩形进行二次检测
  3. 性能优化:对已破碎的砖块区域建立空间索引

结论

Canvas小游戏的碰撞检测体系是一个从简单到复杂的渐进过程。开发者应根据游戏类型、性能需求和开发成本综合选择检测方案:

  • 简单游戏:优先使用AABB矩形检测
  • 中等复杂度:结合圆形检测与矩形检测
  • 高端需求:在关键交互处使用SAT或像素级检测

掌握这些核心检测技术后,开发者可进一步探索物理引擎集成、碰撞响应优化等高级主题,构建出更加流畅真实的游戏体验。