Box2D — Erin Catto C++ 2D 物理
待复核Box2D 是一个经典 2D 刚体物理引擎。日常类比:它像一个“可复用的物理教材引擎”——你负责画角色和规则,它负责每一帧把力、速度、摩擦、碰撞变成可复现的结果。
它的入口常见是:
- 创建
b2World - 定义物体刚体、形状和 fixture
- 每帧调用
Step - 按新物理状态同步到渲染层
这条链条让开发者不用手写积分和碰撞求解。
2D 游戏里“看起来自然”不是渲染特效,而是物理参数和离散时间积分的一致性。
gravity不是只写 9.8:单位、缩放比都要和像素坐标系一致。- 一个小角速度抖动会在 10 秒后放大成整屏抖动。
- 物理 step 稳定性直接决定体验。
- 碰撞过滤、传感器、关节约束写错时,玩家看到的是“穿墙”“抖动”“卡死”,很难靠美术补救。
-
世界与步进要稳定。
b2World::Step(dt, velocityIterations, positionIterations)是核心入口。类比:像节拍器,每拍都按同样时间推进,乐队才不会越演越乱。dt太大:穿透增强,隧穿风险上升。- 迭代次数太低:抖动和堆积穿透明显。
- 迭代过高:性能掉得快但数值更稳定。
-
形状和 fixture 决定“怎么撞”。Circle、Polygon 是最常用,复杂边界可用链条边和胶囊体。类比:同一个玩具人,穿棉衣和穿盔甲,撞到墙的反应不一样。
- 碰撞形状和 fixture 密度决定质量与惯性。
- 传感器 fixture 不参与物理响应,仅作触发。
- filter bits 决定某两类物体是否允许碰撞。
-
关节和约束把自由物体连成机构。Revolute、Prismatic、Distance、Weld 等关节让结构从“自由落体”变成“可控机械”。类比:门铰链只允许绕一个轴转,抽屉滑轨只允许前后滑。
- 锁链系统要控制长度和阻尼。
- 速度太高时要加限位器,避免数值发散。
- 关节误差要用测试场景回归,不要只凭肉眼调一次。
案例 1:基本掉落
Section titled “案例 1:基本掉落”一个球体从高处下落,碰撞地面后反弹:
b2World world({0.0f, -10.0f});world.Step(1.0f / 60.0f, 8, 3);逐部分解释:
b2World保存所有刚体、碰撞和关节,是物理“场地”。{0.0f, -10.0f}是重力方向;如果像素比例不统一,掉落速度会显得怪。Step的三个参数分别是固定时间步、速度迭代、位置迭代;8, 3常作为教学起点。
案例 2:推箱子
Section titled “案例 2:推箱子”角色施加脉冲力推动箱体时,若不设合适质量比例会出现“角色被推飞”或“箱子不动”。
boxBody->ApplyLinearImpulseToCenter({20.0f, 0.0f}, true);逐部分解释:
ApplyLinearImpulseToCenter像“突然推一下”,适合跳跃、撞击、推箱子。- 第二个参数
true会唤醒正在 sleep 的 body,避免推了没反应。 - 箱体密度、摩擦、角色质量要一起调;质量差 10 倍以上时很容易出现不自然反弹。
案例 3:滚球轨道
Section titled “案例 3:滚球轨道”让多个斜面形成轨道,关键是每段坡度和摩擦匹配。
const float fixedDt = 1.0f / 60.0f;accumulator += frameDt;while (accumulator >= fixedDt) { world.Step(fixedDt, 8, 3); accumulator -= fixedDt;}逐部分解释:
frameDt是真实帧间隔,会抖;fixedDt是物理世界的固定节拍。- 用 accumulator 把渲染帧率和物理步进解耦,轨道才不随机器快慢改变。
- 高速滚球仍要开启 CCD 或缩小步长,否则会穿过薄平台。
- 像素和米坐标混用:同一世界里 PTM 比例不一致会导致对象“忽大忽小”。
- 固定步长被忽略:真实帧率波动下直接用
deltaSeconds会导致不稳定。 - 过早回收 body:没先销毁 joint 再 destroy body 可能引发野指针。
- 错误设置 sleep threshold:无休眠对象持续参与计算。
- 接触监听器逻辑副作用:BeginContact 和 EndContact 里改状态前先排队。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 2D 物理小游戏、模拟器、教育演示
- 对性能有控制要求的轻量跨平台项目
- 需要自己扩展碰撞/约束行为的团队
不适用:
- 3D 或大规模物理粒子系统(应用 Box2D 会吃力)
- 无法接受固定步长更新的系统
- 极端高精度刚体仿真(建议更专门引擎)
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 最初定位是让游戏团队避开重复造轮子。
- 后续社区围绕 Cocos / Unity 等生态形成大量封装。
- 在“轻量 2D”与“可控 determinism”之间,它形成了长期标杆。
- 同类里,很多人从 Box2D 过渡到 Chipmunk / Planck,通常是为了生态语言差异。
- 物理引擎要点在于“稳定模型参数”,不是更多特效。
- 碰撞和约束是两个系统:处理接触是物理,处理反应是你定义的游戏逻辑。
- 固定步进是体验可复现的前提。
- 2D 物理的复杂度多数发生在边界场景而不是基础 API。
- 有序的世界构建顺序比参数魔改更重要。
- 官方仓库: https://github.com/erincatto/box2d
- Box2D 文档与示例工程(官方文档)
- chipmunk2d——轻量物理引擎替代路线
- matter-js——JS 端口思路参考
- cocos2d-x——引擎级应用示例
- collision-detection —— 碰撞检测原理
- game-loop —— 固定步进与时间管理
- physics-materials —— 摩擦 / 恢复系数实践
- rigid-body —— 刚体物理基础
- ecs —— 当对象管理规模变大时的结构模式