概述
Box2D是一个用于游戏的2D刚体模拟库。程序员可以在他们的游戏中使用它使物体以真实的方式移动,使游戏世界更具交互性。从游戏引擎的角度来看,物理引擎只是程序动画的一个系统。
Box2D是用便携式C编写的。引擎中定义的大多数类型都以b2前缀开头。希望这足以避免名称与您的游戏引擎冲突。
先决条件
在本手册中,我假设你熟悉基本的物理概念,比如质量、力、力矩和脉冲。如果没有,请先咨询谷歌搜索和维基百科。
Box2D是作为游戏开发者大会物理教程的一部分而创建的。您可以从box2d.org的下载部分获得这些教程。
由于Box2D是用C编写的,因此您应该具有C编程的经验。Box2D不应该是你的第一个C编程项目!您应该熟悉编译、链接和调试。
注意安全:Box2D不应是您的第一个C项目。在使用Box2D之前,请先学习C编程、编译、链接和调试。网上有很多资源。
范围
本手册涵盖了Box2D API的大部分内容。然而,并不是每一个方面都包括在内。请查看Box2D附带的测试台以了解更多信息。
本手册仅在新版本中更新。最新版本的Box2D可能与本手册不同步。
核心概念
Box2D使用几个基本概念和对象。我们在这里简要地定义了这些对象,更多细节将在本文后面给出。
形状
形状是二维几何对象,例如圆或多边形。
刚体
一块非常坚固的物质,其上任何两块物质之间的距离是恒定的。它们像钻石一样坚硬。在下面的讨论中,我们把刚体和刚体互换使用。
固定装置
固定装置将形状绑定到实体并添加材质属性,例如密度、摩擦力和恢复。固定装置将一个形状放入碰撞系统(宽相位),以便它可以与其他形状碰撞。
约束
约束是从实体中移除自由度的物理连接。二维实体有3个自由度(两个平移坐标和一个旋转坐标)。如果我们把一个物体钉在墙上(像钟摆一样),我们就把它约束在墙上。此时实体只能围绕销旋转,因此约束已删除2个自由度。
接触约束
设计用于防止刚体穿透并模拟摩擦和恢复的特殊约束。不创建接触约束;它们由Box2D自动创建。
共同的
这是用于将两个或多个实体固定在一起的约束。Box2D支持多种关节类型:旋转、棱柱形、距离等。有些接头可能有限位和电机。
接头极限
关节限制限制限制关节的运动范围。例如,人的肘部只允许一定范围的角度。
电机接头
关节马达根据关节的自由度驱动连接体的运动。例如,可以使用电动机驱动弯头的旋转。
世界
物理世界是相互作用的实体、固定装置和约束的集合。Box2D支持创建多个世界,但这通常不是必需的或不可取的。
解算器
物理世界有一个解算器,用于推进时间并解决接触和关节约束。Box2D解算器是一种高性能迭代解算器,按N次操作,其中N是约束数。
连续碰撞
解算器使用离散时间步推进物体。如果没有干预,这可能导致隧道开挖。
Box2D包含处理隧道的专门算法。首先,碰撞算法可以对两个物体的运动进行插值,求出碰撞的第一时间(TOI)。第二,有一个子步进解算器,它将物体移动到第一次碰撞的位置,然后解决碰撞问题。
模块
Box2D由三个模块组成:Common、Collision和Dynamics。公共模块有分配、数学和设置的代码。碰撞模块定义形状、宽相位和碰撞函数/查询。最后,动力学模块提供仿真世界、实体、夹具和关节。
单位
Box2D使用浮点数工作,必须使用公差才能使Box2D表现良好。这些公差已调整为与米-千克-秒(MKS)单位配合使用。特别是,Box2D已经过调整,可以很好地处理0.1到10米之间的移动形状。所以这意味着汤罐和公共汽车之间的物体应该能正常工作。静态形状可长达50米,无需麻烦。
作为一个2D物理引擎,使用像素作为单位是很诱人的。不幸的是,这将导致一个糟糕的模拟和可能怪异的行为。一个200像素长的物体可以被Box2D视为45层建筑的大小。
注意安全:Box2D针对MKS单位进行了调整。将移动物体的大小保持在0.1到10米之间。在渲染环境和角色时,需要使用一些缩放系统。Box2D测试台通过使用OpenGL视口转换来实现这一点。不要使用像素。
最好将Box2D实体视为可移动的广告牌,您可以在上面贴上您的艺术品。广告牌可以以米为单位移动,但你可以用一个简单的比例因子将其转换为像素坐标。然后你可以使用这些像素坐标来放置你的精灵,等等。你也可以解释翻转的坐标轴。
另一个需要考虑的限制是全球的总体规模。如果你的世界单位变得大于2公里左右,那么失去的精度会影响稳定性。
注意安全:Box2D在世界尺寸小于2公里的情况下效果最好。使用b2world::Shiftorigin支持更大的世界
如果你需要一个更大的游戏世界,考虑使用b2world::Shiftorigin让世界起源靠近你的玩家。我建议使用网格线和一些滞后来触发ShiftOrigin的调用。这个调用应该很少进行,因为它有CPU开销。在游戏单元和Box2D单元之间转换时,可能需要存储物理偏移量。
Box2D使用弧度表示角度。身体的旋转以弧度存储,并且可能无限增长。如果角度的大小变得过大,请考虑将身体的角度规格化(使用b2Body::SetTransform
).
注意安全:Box2D使用弧度,而不是度数。
更改长度单位
高级用户可以修改长度单位b2_lengthUnitsPerMeter
. 您可以通过定义B2用户设置
并提供b2_user_settings.h
. 查看文件b2_设置.h
了解详情
工厂和定义
快速内存管理在box2dapi的设计中起着核心作用。所以当你创建一个b2Body公司或者B2接头,需要调用工厂函数b2World公司. 您不应该尝试以其他方式分配这些类型。
有创建功能:
b2Body* b2World::CreateBody(const b2BodyDef* def)
b2Joint* b2World::CreateJoint(const b2JointDef* def)
并有相应的销毁功能:
void b2World::DestroyBody(b2Body* body)
void b2World::DestroyJoint(b2Joint* joint)
创建实体或关节时,需要提供定义。这些定义包含构建实体或关节所需的所有信息。通过使用这种方法,我们可以防止构造错误,保持函数参数的数量较小,提供合理的默认值,并减少访问器的数量。
由于固定装置(形状)必须是实体的父对象,因此使用上的工厂方法创建和销毁它们b2Body公司 :
b2Fixture* b2Body::CreateFixture(const b2FixtureDef* def)
void b2Body::DestroyFixture(b2Fixture* fixture)
还有一种直接从形状和密度创建夹具的快捷方式。
b2Fixture* b2Body::CreateFixture(const b2Shape* shape, float density)
工厂不保留对定义的引用。因此,您可以在堆栈上创建定义并将它们保存在临时资源中。