JavaScript 分离轴碰撞检测教程

一、引言

在游戏开发或物理仿真中,碰撞检测是非常重要的功能之一。本篇文章将教你如何使用 JavaScript 实现“分离轴碰撞检测”算法。它是一种高效、广泛应用于2D物体之间碰撞检测的算法。接下来,我们将通过分步过程深入分析。

二、整体流程

在实现分离轴碰撞检测之前,我们首先了解实现的步骤。下表展示了整个流程:

步骤 描述
1. 确定碰撞的形状 确定将要检测的物体的形状(如矩形、圆等)
2. 提取顶点 从每个形状中提取出它的顶点坐标
3. 计算法向量 为每一条边计算法向量,以确定分离轴
4. 投影坐标 将所有顶点投影到分离轴上
5. 检查重叠 检查在每个投影上的重叠情况,以判断是否发生碰撞
6. 返回结果 返回碰撞检测的结果

三、详细步骤解析

步骤 1: 确定碰撞的形状

首先,我们需要确定要检测的形状。这里我们以两个矩形为例。

步骤 2: 提取顶点

我们需要提取出矩形的四个顶点。

function getVertices(rect) {
  return [
    { x: rect.x, y: rect.y }, // 左上角
    { x: rect.x + rect.width, y: rect.y }, // 右上角
    { x: rect.x + rect.width, y: rect.y + rect.height }, // 右下角
    { x: rect.x, y: rect.y + rect.height } // 左下角
  ];
}

注释:该函数返回一个矩形的四个顶点坐标。

步骤 3: 计算法向量

通过获取每一条边的法向量,以下是计算法向量的代码:

function getNormalEdges(vertices) {
  let edges = [];
  
  for (let i = 0; i < vertices.length; i++) {
    const nextIndex = (i + 1) % vertices.length; // 获取下一个顶点索引
    const edge = {
      x: vertices[nextIndex].x - vertices[i].x,
      y: vertices[nextIndex].y - vertices[i].y
    };
    // 计算法向量
    edges.push({ x: -edge.y, y: edge.x });
  }
  
  return edges;
}

注释:此函数接收一个顶点数组,获取每一条边,并计算法向量。

步骤 4: 投影坐标

对每个顶点进行投影,以确定它在法向量上的位置。

function projectVertices(vertices, axis) {
  let min = Infinity, max = -Infinity;
  
  for (let vertex of vertices) {
    const projection = (vertex.x * axis.x + vertex.y * axis.y);
    min = Math.min(min, projection);
    max = Math.max(max, projection);
  }
  
  return { min, max };
}

注释:函数将顶点投影到给定轴上,并返回投影的最小值和最大值。

步骤 5: 检查重叠

我们需要检查两个形状的投影是否重叠。

function overlapOnAxis(proj1, proj2) {
  return !(proj1.max < proj2.min || proj2.max < proj1.min);
}

注释:此函数判断两个投影区间是否重叠。

步骤 6: 返回结果

最后,我们将上述步骤组合到一起,形成完整的碰撞检测函数。

function isColliding(rect1, rect2) {
  const vertices1 = getVertices(rect1);
  const vertices2 = getVertices(rect2);
  
  const edges1 = getNormalEdges(vertices1);
  const edges2 = getNormalEdges(vertices2);
  
  for (const edge of [...edges1, ...edges2]) {
    const projection1 = projectVertices(vertices1, edge);
    const projection2 = projectVertices(vertices2, edge);
    
    if (!overlapOnAxis(projection1, projection2)) {
      return false; // 没有发生碰撞
    }
  }
  
  return true; // 发生了碰撞
}

注释:这是整个碰撞检测的实现,判断两个矩形是否碰撞。

四、关系图

在编写代码的过程中,我们可以通过简单的 ER 图理解不同组成部分的关系:

erDiagram
    RECTANGLE {
        STRING id
        FLOAT x
        FLOAT y
        FLOAT width
        FLOAT height
    }
    COLLISION {
        STRING rect1_id
        STRING rect2_id
        BOOLEAN isColliding
    }

五、饼状图

为了更好地理解碰撞检测的应用,我们可以用饼状图表示不同形状的碰撞检测结果分布:

pie
    title 碰撞检测结果分布
    "无碰撞": 70
    "发生碰撞": 30

六、结尾

通过上述步骤,我们完全实现了利用 JavaScript 进行分离轴碰撞检测的方法。这种方法简单而高效,被广泛应用于2D游戏开发和物理引擎中。希望这篇文章能为你提供一个良好的开始,鼓励你进一步探索碰撞检测和物理模拟的更深层次内容。祝你在编程的道路上越走越远!