导读:本期聚焦于小伙伴创作的《如何利用 JavaScript 实现一个简单的物理引擎模拟碰撞和运动?》,敬请观看详情。刚接触前端动画开发时,常误以为物体碰撞只是改变坐标那么简单,实际上需要考虑速度、加速度与反弹系数。本文从牛顿力学基础讲起,用 JavaScript 构建包含重力、匀速运动及边界碰撞回弹的轻量模拟器。我们会拆解矢量运算、时间步长积分方式,并对比离散检测与连续检测在穿透问题上的差异。通过代码示例展示如何用 requestAnimationFrame 驱动循环,以及如何用轴对称反弹处理矩形与墙壁的碰撞。掌握这些要点后,你便能扩展出多体碰撞与摩擦力效果,而不必依赖庞大第三方库。

用 JavaScript 在浏览器里还原真实世界的物体运动,核心是把物理学里的位移、速度和加速度关系转化为每帧更新的数值计算。很多交互网页里的弹球、掉落方块其实就是一套微型物理引擎在背后跑循环。我们不需要一上来就搬出复杂的刚体动力学,只要搞懂二维平面上的质点运动与轴对称反弹,就能做出可玩的碰撞模拟。

如何利用 JavaScript 实现一个简单的物理引擎模拟碰撞和运动?

运动学基础与矢量运算的实现

在二维空间里,物体的位置、速度和加速度都可以用含有 x 与 y 分量的矢量表示。物理学公式位置等于初位置加速度乘时间加二分之一加速度乘时间平方,在代码里通常被拆成每个坐标轴单独累加,这样更方便处理。我们会用一个对象保存物体的状态,包括坐标、速度以及质量等属性,然后在每一帧根据时间差更新它们。

时间步长一般采用上一次绘制到本次绘制的间隔,但为了稳定,很多简单引擎会固定步长,比如假设每秒六十帧,每帧对应十六点七毫秒。下面这段代码展示了如何定义物体结构并利用欧拉积分更新位置。欧拉积分是最直接的数值积分方式,它用当前速度推算下一帧位置,虽然不够精确但足够应付轻量模拟。

// 定义一个运动物体
function createBody(x, y, vx, vy, radius) {
    return {
        x: x,
        y: y,
        vx: vx,
        vy: vy,
        radius: radius,
        mass: 1
    };
}

// 使用固定步长更新位置
function updateBody(body, dt) {
    // dt 为秒,例如 0.016
    body.x = body.x + body.vx * dt;
    body.y = body.y + body.vy * dt;
    // 这里暂未加入加速度,后面扩展重力时再补充
}

上面的 createBody 函数把物体抽象成带有横纵坐标和分速度的质点,updateBody 则是最基础的运动推进。实际开发中,我们会把加速度也放进更新逻辑,比如重力就是给 vy 持续加上一个常数。矢量运算的优势在于 x 与 y 完全独立,遇到斜向运动也不会增加复杂度。

边界碰撞检测与反弹处理

当物体飞出画布或者撞到墙壁时,最简单的方法是判断坐标是否越界,若越界就把对应方向的速度取反,并让物体回到边界内。这种方法叫离散碰撞检测,它每帧检查一次状态,实现容易,但在速度过快时可能穿过薄墙,也就是穿透现象。对于简单演示,穿透可接受,若要求严格则要采用连续检测或缩小步长。

下面示例在画布宽高范围内让圆球碰到四边就反弹,同时引入了重力加速下落。反弹系数用变量存储,值小于一表示有能量损耗。注意代码中比较的是物体圆心加减半径后的边界,这样才能让球体表面而非圆心贴合墙壁。

const canvasWidth = 800;
const canvasHeight = 400;
const restitution = 0.8; // 反弹保留百分之八十速度

function handleBoundary(body) {
    if (body.x - body.radius < 0) {
        body.x = body.radius;
        body.vx = -body.vx * restitution;
    } else if (body.x + body.radius > canvasWidth) {
        body.x = canvasWidth - body.radius;
        body.vx = -body.vx * restitution;
    }

    if (body.y - body.radius < 0) {
        body.y = body.radius;
        body.vy = -body.vy * restitution;
    } else if (body.y + body.radius > canvasHeight) {
        body.y = canvasHeight - body.radius;
        body.vy = -body.vy * restitution;
    }
}

// 在每帧中先加重力再更新再处理边界
function step(body, dt) {
    body.vy = body.vy + 980 * dt; // 重力近似 980 像素每平方秒
    updateBody(body, dt);
    handleBoundary(body);
}

这种边界方案只适合物体与轴对齐的静态墙面互动。它的优点是计算量极小,手机浏览器也能轻松跑几十个小球。缺点是无法处理两个运动物体之间的互相碰撞,那需要引入碰撞法向量与动量守恒计算,我们下一个小节来补足这块。

多物体碰撞与动量交换原理

如果两个圆球在运动中相遇,就要检测它们圆心距离是否小于半径之和。一旦相交,就说明发生碰撞。处理这种碰撞不能简单把速度反向,而要根据两球连心线方向分解速度,再按质量做一维弹性或非弹性碰撞公式交换法向分量,切向分量保持不变。这样球才会看起来一个撞开另一个而不是穿过去。

以下代码演示了两圆碰撞的检测与基于一维冲量法的响应。我们假设两球质量相同,碰撞完全弹性,仅交换法向速度。实际项目里会把质量比带入公式,并用 restitution 控制非弹性损耗。这种算法属于圆形碰撞,因为圆没有旋转和角落,计算最省事,是入门物理引擎的最佳练习。

function resolveCollision(a, b) {
    const dx = b.x - a.x;
    const dy = b.y - a.y;
    const dist = Math.sqrt(dx * dx + dy * dy);
    const minDist = a.radius + b.radius;
    if (dist === 0 || dist >= minDist) return;

    // 法向单位向量
    const nx = dx / dist;
    const ny = dy / dist;

    // 相对速度在法向上的投影
    const rvx = b.vx - a.vx;
    const rvy = b.vy - a.vy;
    const velAlongNormal = rvx * nx + rvy * ny;
    if (velAlongNormal > 0) return; // 已经在分开

    // 相同质量弹性碰撞,交换法向速度分量
    const aNormal = a.vx * nx + a.vy * ny;
    const bNormal = b.vx * nx + b.vy * ny;
    const temp = aNormal;
    a.vx = a.vx - aNormal * nx + bNormal * nx;
    a.vy = a.vy - aNormal * ny + bNormal * ny;
    b.vx = b.vx - bNormal * nx + temp * nx;
    b.vy = b.vy - bNormal * ny + temp * ny;

    // 简单分离防止粘连
    const overlap = minDist - dist;
    a.x = a.x - nx * overlap / 2;
    b.x = b.x + nx * overlap / 2;
}

把前面单球边界逻辑和多球碰撞结合,再用 requestAnimationFrame 循环调用 step 与绘制,就得到一个能模拟重力、墙壁回弹和球间碰撞的小引擎。若要更真实,可加入空气阻力、旋转摩擦力,或把圆扩展成矩形并用分离轴定理处理。理解了这些底层模块,你就能读懂大部分开源二维物理库的设计思路,也能按需求自己裁剪功能,不必被庞大框架束缚。

JavaScript物理引擎碰撞检测修改时间:2026-08-13 07:42:33

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。