7.1Box2D概述
Box2D是一款用于2D游戏的物理引擎。在Box2D物理世界里,创建出的每个物体都更接近于真实世界中的物体,让游戏中的物体运动起来更加真实可信,让游戏世界看起来更具交互性。 Android平台常见的十几款游戏引擎中,例如:Rokon、AndEngine、libgdx等物理引擎都封装了Box2D物理引擎,可见Box2D在物理引擎中占据了多重要的位置。
Box2D在很多平台都有对应的版本:Flash版本、Iphone版本、Java版本(JBox2D)等等,在本书中开发Android语言采用的是Java,所以这里介绍的对应Box2D平台也是Java平台,称为JBox2D,对应的版本为JBox2d 2.0。
7.2将Box2D类库导入Android项目中
添加Box2D类库到Android项目详细步骤如下:
新建项目“HelloBox2d”(创建Android项目与之前创建项目步骤无差异)。
然后在项目里添加一个“lib”目录用于存放Box2D类库(“.jar”)。
右键项目“HelloBox2d”,选中“New”→“Folder”选项,然后出现“New Folder”窗口,如
图7-1所示。
图7-1 New Folder
图7-1所示的窗口中,在“Folder name”文本框中填写目录名,这个目录名可以自定义,一般导入外部类库目录都使用lib与libs来命名,然后单击“Finish”按钮完成目录的创建。
此时只是将Box2D提供的类库放入Android项目中,但是并没有添加到项目的类库中,所以还需要将Box2D包添加到项目类库中!
右击项目“HelloBox2d”选中“Properties ”→“Java Build Path”→“Libraries”→“Add
<”,选中“HelloBox2d”项目下lib目录下的Jbox2d类库的jar包,最后一直单击
“OK”按钮返回即可,如图7-2、图7-3所示。
图7-2 项目配置
图7-3 项目中添加Box2d类库
到此,在Android项目中添加JBox2D物理引擎类库的步骤就结束了,现在就开始进入Box2D引擎的物理世界吧!
7.3物理世界与手机屏幕坐标系之间的关系
本节来讲解一下物理世界与手机屏幕坐标系之间的关系。假设创建一个200米的物理世界,然后观察其物理世界与手机屏幕之间的坐标系关系,如图7-4所示。
图7-4 物理世界与手机屏幕坐标系关系图
从图7-4中可以很清晰的看出,手机屏幕的左上角(0,0)坐标,正是物理世界的中心点坐标;手机屏幕绘制图形时,一般默认以左上角作为锚点!而在Box2d的物理世界中,一个新的Body(物体)等被创建出来之后,默认以其质心(可以近似为中心点)作为锚点;如图7-5所示,是“在屏幕上绘制一张图片,并且在物理世界中添加一个物体”的位置关系图。
图7-5 默认放置的位置对比图
除此之外,Box2D为了使物体与关节等更加贴切的模拟现实,在Box2D引擎中使用的长度单位是“米(m)”,所以Box2D引擎中的一些方法的长度参数不再是以像素为单位,而是需要转换成“米”;反之,从Box2D引擎函数返回值中得到的长度值也是以“米”做单位的,使用其值前需要将其转换为像素,然后再使用。
关于米与像素之间的换算关系,其实是通过自定义一个现实与屏幕的比例关系进行换算的,后续章节会有讲解。
7.4创建Box2D物理世界
World(jbox2d.dynamics.World)类就是Box2D引擎中的物理世界。不管是Body(物体)还是Joint(关节)都必须放在Box2D这个物理世界中,因为物理世界也是有范围的,一旦物体和关节不在世界范围内,它们将不会进行物理模拟。
下面就来创建一个物理世界,范例项目对应的源代码为“7-4(Box2d物理世界)”。
首先看一下World类的构造函数:
World(AABB world AABB,Vec2 gravity,boolean doSleep)
World类只有这一种构造方式,它的三个参数的含义如下:
l 第一个参数:AABB类的实例,AABB表示一个物理模拟世界的范围;
l●第二个参数:Vec2实例,一个二维世界向量类,在Box2D中的最常用的一种数据类型;在这里表示物理世界的重力方向;
l●第三个参数:布尔值,表示在物理世界中,如果静止不动的物体是否对其进行休眠。
如果设置其值为“true”,则表示当物理世界开始进行模拟时,在这个物理世界中静止没有运行的物体都将进行休眠,除非物体被施加了力的作用或者与其他物体发生碰撞之后会被唤醒;如果设置其值设置为“fal”,那么物理世界中的所有物体不管是否静止都会一直进行物理模拟。
创建一个物理世界代码如下:
AABB aabb = new AABB();// 实例化物理世界的范围对象
aabb.lowerBound.t(-100, -100);// 设置物理世界范围的左上角坐标
aabb.upperBound.t(100, 100);// 设置物理世界范围的右下角坐标
Vec2 gravity = new Vec2(0, 10);// 实例化物理世界重力向量对象
World world = new World(aabb, gravity, true);// 实例化物理世界对象
以上代码中有两点需要注意:
(1)aabb设置物理世界范围传入的参数,不要理解成像素!在Box2d的物理世界中,被认为是现实生活中的“米(m)”单位。
(2)设置物理世界的重力向量(gravity),其两个参数在这里分别表示物理世界中的X轴与Y轴方向上的重力数值,其值的“+”“−”号在这里表示X与Y轴的重力方向,X轴正值表示向右,Y轴正值表示向下;因为是模拟真实世界,所以这里的X重力向量设置为零,Y 轴方向设置为现实生活中的重力值:10(可以理解为10N)。
刚才的一段代码就已经创建了一个物理世界,但只是定义了物理世界,并没有开始进行物理模拟,所以还需要world设置物理模拟:
world.step(float timeStep, int iterations);
此函数表示让物理世界开始进行物理模拟,其两个参数含义如下:
l●第一个参数:表示(时间步)物理世界模拟的频率;
l●第二个参数:表示(迭代值)迭代值越大模拟越精确,但性能越低。
这里要注意以下几点:
①因为物理世界模拟具有持续性,所以应该将设置放在线程中,不断的让物理世界进行模拟。
②时间步:应该与游戏的刷新率相同,否则物理世界模拟将不同步。
③迭代值:可以理解为在单次时间步中进行遍历模拟运算数据的次数。
④在Box2D中最常使用的单位是float浮点数类型,作者刚接触Box2D时,在定义物理世界模拟频率时,写成了以下错误的形式: