本文是对Box2D v2.1.2 manual的中文翻译(现在网上流行的一个中文翻译是针对以前版本的,跟最新的2.1.2版有些不对应)的第一章。
1. 导言
1.1 关于
Box2D是一个用于游戏的2D刚体仿真库。程序员可以在游戏里使用它,它可以使物体的运动更加可信,让世界看起来更具交互性。从游戏的视角来看,物理引擎就是一个程序动画系统。
Box2D是用可移植的C++来写成的。引擎中定义的大部分类型都有b2前缀,希望这能消除它和您的游戏引擎之间的命名冲突。
1.2 必备条件
在此,我假定你已经熟悉了基本的物理学概念,例如质量,力,扭矩和冲量。如果没有,请先考虑读一下Chris Hecker和David Baraff(google这些名字)的入门教程,你不需要了解得非常细致,但他们可以使你很好地了解一些基本概念,以便你使用 Box2D。
Wikipedia也是一个极好的物理和数学知识的获取源,在某些方面它可能比google更有用,因为它的内容经过了精心的整理。
Box2D在Game Developer Conference上作为物理教程的一部分而创建。你能从box2d.org的下载部分获得这些教程。
因为Box2D是使用C++编写的,所以你应该具备C++程序设计的经验,Box2D不应该成为你的第一个C++程序项目。你应该已经能熟练地编译,链接和调试了。
1.3 关于手册
这本手册覆盖了大多数的Box2D API。然而,并没有覆盖每个方面。你可以阅读Box2D自带的testbed程序来学习更多知识。
1.4 反馈和错误报告
如果你想反馈Box2D的任何内容,请在论坛里留下意见。这也是一个社区讨论的好地方。Box2D问题跟踪使用Google code project:http://code.google.com/p/box2d/。
1.5 核心概念
Box2D中有一些基本的对象。这里我们先做一个简要的定义,在随后的文档里会有更详细的描述。
shape
一个2D几何对象,例如一个圆圈,或多边形。
rigid body
一块十分坚硬的物质,它上面的任何两点之间的距离都是完全不变的。它们就像钻石那样坚硬。在后面的讨论中,我们用body来代替rigid body。
fixture
fixture绑定shape到body上,并且添加材料属性,比如密度、摩擦和恢复系数。
constraint
constraint就是消除body自由度的物理连接。在2D中,body有3个自由度(两个平移坐标和一个旋转坐标)。如果我们把一个物体钉在墙上(像摆锤那样),那我们就把它约束到了墙上。这样,此物体就只能绕着钉子旋转,所以这个constraint消除了它两个自由度。
contact constraint
一个防止rigid body穿透,以及用于模拟摩擦和恢复的特殊约束。你永远都不必创建一个contact constraint,它们会自动被Box2D创建。
joint
它是一种用于把两个或多个物体固定到一起的约束。Box2D支持的关节类型有:旋转(revolute),棱柱(prismatic),距离(distance)等等。关节可以支持限制(limit)和马达(motor)。
joint limit
joint limit限定了一个关节的运动范围。例如人的胳膊肘只能做某一范围角度的运动。
joint motor
joint motor能依照关节的自由度来驱动所连接的物体。例如,你可以使用motor来驱动一个肘的旋转。
world
world就是body,fixture和constraint相互作用的集合。Box2D支持创建多个世界,但这通常是不必要的。
1.6 模块
Box2D由三个模块组成:Common、Collision、和Dynamics。Common模块包含有内存分配、数学和设置的代码。Collision模块定义了shape、broad-phase和碰撞功能/查询。最后,Dynamics模块提供对world、body、fixture和joint的模拟。
1.7 单位
Box2D使用浮点数,所以必须使用一些公差来保证它正常工作。这些公差已经被调整得能够与“米-千克-秒”单位良好地协同工作。尤其是,Box2D被调整得能够良好地在0.1到10米之间移动物体。这意味着从罐头盒到公共汽车大小的对象都能良好地工作。静态物体可能达到50米而不会出现麻烦。
作为一个2D物理引擎,如果能使用像素作为单位是很诱人的。很不幸,那将导致不良模拟,也可能会造成古怪的行为。一个200像素长的物体在Box2D看来就有45层建筑那么大。想象一下使用一个被调整好模拟玩偶和木桶的引擎去模拟高楼大厦的运动。那并不有趣。
最好把Box2D body(物体)看作移动的广告板,在其上您可以附着您的艺术品。广告板在一个以米为单位的系统里运动,但你可以利用简单的比例因子把它转换为像素坐标。然后,你就可以使用这些像素坐标的放置你的精灵,等等。
Box2D使用弧度作为角度单位。物体旋转角度以弧度方式存储,并且可能无限生长。如角度规模太大,可考虑规范角度的大小(使用b2Body:SetAngle)。
1.8 工厂和定义
内存管理在Box2D API的设计中担当着一个中心角色。所以当你创建一个b2Body或一个 b2Joint的时候,你需要调用b2World的工厂函数。你不应当以另一种方式为这些类型分配空间。
这些是创建函数:
b2Body* b2World::CreateBody(const b2BodyDef* def)
b2Joint* b2World::CreateJoint(const b2JointDef* def)
这些是对应的销毁函数:
void b2World::DestroyBody(b2Body* body)
void b2World::DestroyJoint(b2Joint* joint)
当你创建一个body或joint的时候,你需要提供一个定义。这些定义包含了创建body或joint所有相关信息。通过这样的方法,我们就能预防构造错误,使用少的函数参数,提供有意义的默认参数,并减少访问子的数量。
因为fixture必须有父body,所以b2Body上有创建和摧毁fixture的工厂方法:
b2Fixture* b2Body::CreateFixture(const b2FixtureDef* def)
void b2Body::DestroyFixture(b2Fixture* fixture)
这也是一个创建直接从shape和density创建fixture的方法:
b2Fixture* b2Body::CreateFixture(const b2Shape* shape, float32 density)
1.9 用户数据
b2Fixture,b2Body和b2Joint类都允许你通过一个void指针来附加用户数据。这在你测试Box2D数据结构,以及你想把它们关联到自己的游戏引擎中时是较方便的。
举个典型的例子,在角色上的rigid body中附加角色指针,这就构成了一个循环引用。如果你有角色,你就能得到rigid body。如果你有rigid body,你就能得到角色。
GameActor* actor = GameCreateActor();
b2BodyDef bodyDef;
bodyDef.userData = actor;
actor->body = box2Dworld->CreateBody(&bodyDef);
这是一些需要用户数据的案例:
l 使用碰撞结果给角色施加伤害。
l 当玩家进入一个包围盒时播放一段脚本事件。
l 当Box2D通知你一个关节即将摧毁时访问一个游戏结构。
用户数据是可选的,并且能放入任何东西。然而,你需要保持一致性。例如,如果你想在body中保存一个角色的指针,那你就应该在所有物体中都保存一个角色指针。不要在一个body中保存角色指针,却在另一个body中保存其它指针。这可能会导致程序崩溃。