声明: 本系列文章使用的Libgdx版本均为 0.99 版本 Libgdx游戏开发交流群 323876830 Box2D的是一个物理引擎库。他是一个用于2D的最流行的物理引擎库中的一个,被用到了许多语言,各种引擎中, 其中就包括咱们使用的这个Libgdx。 Libgdx中的Box2D实现了对于C
声明:
本系列文章使用的Libgdx版本均为0.99版本
Libgdx游戏开发交流群 323876830
Box2D的是一个物理引擎库。他是一个用于2D的最流行的物理引擎库中的一个,被用到了许多语言,各种引擎中,
其中就包括咱们使用的这个Libgdx。
Libgdx中的Box2D实现了对于C++引擎的java包装,所以参考文档也可以直接使用C++版本的,official Box2D manual (PDF)
想获得更多的信息可以到Box2D的官网,box2d.org 获得信息。
这里我找到一个中文翻译的v2.0.1版本,这里要谢谢译者Aman JIANG(江超宇)。
文档下载地址
但是v2.0.1的版本比较老了,有一些概念都不一样了,例如2.1世界没有了包围盒,绑定形状到物体有了FixtureDef的概念,
这里有一个v2.1.0的翻译版本
http://blog.csdn.net/complex_ok/article/category/871440
特别推荐Box2D教程 http://www.iforce2d.net/b2dtut/introduction
对应中文译文 http://ohcoder.com/blog/categories/box2d-tutorials/
创建一个世界
物理世界,那么首先需要创造一个世界,他是物理物体,作用力的基础。但是他不会帮我们绘制物体, 需要我们使用Libgdx
绘制的相关api来进行绘制。 也就是说我们获取物体的坐标,旋转信息什么的,然后自己绘制。 但是他在debug模式下,可以
绘制自己的物理模拟信息,形状啦,什么的。
可以向下面一样创建一个世界
World world = new World(new Vector2(0, -10), true);
第一个参数是一个2维向量,0说明水平方向的重力为0, 10说明垂直方向的重力为10. 因为他是一个y轴向上的坐标系,所以负号
代表向下。这个和opengl的世界坐标系是一致的。当然你也可以改成你想要的值, 不过要注意比例,在Box2D中 1单元=1米。
第二个参数的意思是我们创造的世界要不要让物体休眠,休眠可以减少cpu的使用, 具体休不休眠看你的情景了。
这里的比例最好和绘制的保持一致,也就是我们的opengl绘制。这样精灵,镜头的单位都统一。
Debug绘制
如果我们想开启debug绘制, 可以这样
mDebugRender = new Box2DDebugRenderer();
mDebugRender.render(mWorld, mCam.combined);
这里要注意一下, 当我们发布的时候要注释掉 , 我测试了一个例子, 在开启状态只有十几帧,关闭掉能够达到
五六十帧的,还是很好性能的。
时间步
想要我们的物理模拟动起来, 需要我们告诉他。最好调用它的位置在reader的最后面。
最好给他的帧率是一样的,不要使用绘制的帧率,像这样
world.step(1/60f, 6, 2);
第一个参数是时间步,也就是我们想让世界模拟的时间。
在大部分情况下, 这个时间步都应该是固定的, Libgdx推荐在移动手机1/45f或者1/300f.
另外两个参数是velocityIterations,positionIterations 速度的约束求解量 和 位置的约束求解量.
约束求解器用于解决模拟中的所有
约束,一次一个。单个的约束会被完美的求解,然而当我们求解一个约束的时候,我们就会稍微耽误另
一个。要得到良好的解,我们需要迭代所有约束多次。建议的 Box2D 迭代次数是 10 次。你可以按自己
的喜好去调整这个数,但要记得它是速度与质量之间的平衡。更少的迭代会增加性能并降低精度,同样
地,更多的迭代会减少性能但提高模拟质量。
绘制
推荐的做法是在step之前绘制我们的图形, 否则将会出现不同步的问题。
如果想要debug绘制, 可以使用
debugRenderer.render(world, camera.combined);
第一个参数是世界,第二个是镜头矩阵
Body物体
现在如果我们运行我们的代码,将会什么也看不到,虽然我们的世界步执行着呢, 这是因为我们没有放入
任何物体进去。 所以, 接下来我们会放进去一些物体。
在Box2D中, 对象叫做物体, 物体包含许多固定物fixtures,他可以固定物体的位置,方向等, 固定物可以是
任何形状, 可以组合多个不同的固定物来组成物体。
固定物包含形状, 密度, 摩擦力,恢复力。形状就是集合图形了, 密度是每立方米的物体的质量, 就比如保龄球
和氢气球,密度大密度小。摩擦力就是物体接触移动产生的力,在冰上移动和在橡胶上, 摩擦力显而易见了。
恢复力就是有多大的弹性, 石头的弹性就比较小 , 乒乓球的弹性就比较大。 当一个物体的弹性是0的时候, 当接触到
地面就会停止,当为1的时候, 他会反弹到他原来的高度。
物体有三种类型:Dynamic动态的,Static静态的,Kinematic介于他们之间的运动物体。
动态物体
动态物理可以四处移动, 他受力的作用和其他动态物体,静态物体, 运动物体的作用。
他适合那种需要移动然后受到力的作用的物体。
现在我们学习固定物的创建, 来构造物体
// First we create a body definition BodyDef bodyDef = new BodyDef(); // We set our body to dynamic, for something like ground which doesn't move we would set it to StaticBody bodyDef.type = BodyType.DynamicBody; // Set our body's starting position in the world bodyDef.position.set(100, 300); // Create our body in the world using our body definition Body body = world.createBody(bodyDef); // Create a circle shape and set its radius to 6 CircleShape circle = new CircleShape(); circle.setRadius(6f); // Create a fixture definition to apply our shape to FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = circle; fixtureDef.density = 0.5f; fixtureDef.friction = 0.4f; fixtureDef.restitution = 0.6f; // Make it bounce a little bit // Create our fixture and attach it to the body Fixture fixture = body.createFixture(fixtureDef); // Remember to dispose of any shapes after you're done with them! // BodyDef and FixtureDef don't need disposing, but shapes do. circle.dispose();
现在我们创建了一个球的物体到我们的世界。运行的时候可以看到他将会下落, 但是感觉还是没多少意思,
没有力的作用, 下面我们来加入静态物体地板。
静态物体
静态物体是一个不能移动, 也不受力的作用的物体。动态物体接触它, 动态物体会有力的作用的。静态物体
让他来做为地板,墙壁等不能动的物体是非常合适的。 静态物体也消耗比较少的计算量。
让我们来创建一个静态物体, 跟我们前面的创建动态物体的代码比较像
// Create our body definition BodyDef groundBodyDef =new BodyDef(); // Set its world position groundBodyDef.position.set(new Vector2(0, 10)); // Create a body from the defintion and add it to the world Body groundBody = world.createBody(groundBodyDef); // Create a polygon shape PolygonShape groundBox = new PolygonShape(); // Set the polygon shape as a box which is twice the size of our view port and 20 high // (setAsBox takes half-width and half-height as arguments) groundBox.setAsBox(camera.viewportWidth, 10.0f); // Create a fixture from our polygon shape and add it to our ground body groundBody.createFixture(groundBox, 0.0f); // Clean up after ourselves groundBox.dispose();
我们怎么样不适用FixtureDef创造一个夹具呢?如果我们只有形状和密度, createFixture有个重载方法,调用它就可以了。
现在我们看到, 球下落到我们的地面上, 然后反弹, 最终停止在地面上, 我们也可以更改其中的值来看看有什么其他效果。
运动物体
Kinematic物体是一个介于在静态和动态物体之间的物体。 像静态物体,他们不受力的作用, 像动态物体,他们可以移动。
应用的场景例如移动的平台等。
我们可以直接的调用Kinematic物体的位置,直接更改他的位置, 但是最好的方式是设置一个速度,让Box2D自己来更改
他的坐标。
我们可以使用上面创建动态和静态物体的方式一样来创建这个物体, 一旦我们创建好, 我们可以像下面一样来控制他
// Move upwards at a rate of 1 meter per second kinematicBody.setLinearVelocity(0.0f, 1.0f);
好了, 运行程序, 我们发现, 我们的物体在朝着一个固定的方向移动, 当我们的小球遇到咱们这个方块的时候,由于力
的作用,朝右方向移动了,但是它与静态物体地板接触的时候, 并没有受到力的作用, 当然地板也没有力的作用。
冲量与力
冲量和力用来移动物体, 但是不会改变重力和碰撞检测。
力是一个逐渐改变物体速度的过程, 比如火箭的升起,是有一个力的驱动,逐渐改变火箭的速度, 慢慢升起的,越来
越快。
冲量就不同了,可以瞬间改变物体速度,比如吃豆人游戏,角色都是在一个恒定的速度移动,改变也是瞬间的。
首先我们需要一个动态的物体,就使用上面那个吧。
应用力
力是单位是牛顿。 如果力没有作用在质量的中心,那么将会产生一个扭矩, 产生一个带有夹角的速度。
// Apply a force of 1 meter per second on the X-axis at pos.x/pos.y of the body slowly moving it right dynamicBody.applyForce(1.0f, 0.0f, pos.x, pos.y, true); // If we always want to apply force at the center of the body, use the following dynamicBody.applyForceToCenter(1.0f, 0.0f, true);
应用冲量
冲量就像一个特殊的力,只是冲量可以立即改变物体的速度。 同样的, 如果冲量没有作用在物体的中间, 也将会产生
一个扭矩, 产生一个带有夹角的速度。冲量的单位是 牛顿每秒 或者 kg-m/s.
// Immediately set the X-velocity to 1 meter per second causing the body to move right quickly dynamicBody.applyLinearImpulse(1.0f, 0, pos.x, pos.y, true);
有一点要注意一下,应用力和冲量会唤醒物体, 有时候这不是我们想要的, 比如, 你想作用一个稳定的力,想让物体在睡眠中执行,
这种情况下, 我们可以这样
// Apply impulse but don't wake the body dynamicBody.applyLinearImpulse(0.8f, 0, pos.x, pos.y, false);
Joints关节
占位
Fixture Shapes形状
占位
Sprites and Bodies 精灵与物体
最简单的方式管理精灵语物体的是使用Box2D的用户数据,我们可以使用用户数据改变游戏对象的位置坐标, 旋转等信息。
设置用户信息也比较简单,可以这样
body.setUserData(Object);
这个用户数据可以是任何java对象, 一个比较好的方式是建立一个游戏对象然后放入用户数据,这样以后可以使用。
Fixtures夹具也可以设置用户数据,和上面一样。
想更新我们的角色或精灵的信息, 可以遍历所有的世界物体,取出对应的用户数据,然后更新信息, 示例
Iterator bi = world.getBodies(); while (bi.hasNext()){ Body b = bi.next(); // Get the bodies user data - in this example, our user // data is an instance of the Entity class Entity e = (Entity) b.getUserData(); if (e != null) { // Update the entities/sprites position and angle e.setPosition(b.getPosition().x, b.getPosition().y); // We need to convert our angle from radians to degrees e.setRotation(MathUtils.radiansToDegrees * b.getAngle()); } }
绘制部分还是和以前一样的。 没有什么改动。
Sensors传感器
传感器是当物体间碰撞,而又不产生自动响应。 比如, 我们想知道两个物体重叠的事件。
这里需要设置'isSensor'为true。像这样
//At the definition of the Fixture fixtureDef.isSensor = true;
想监听它,需要实现ContactListener接口,在对应方法中实现自己的逻辑。
传感器接口
传感器接口监听fixture夹具的碰撞,方法包含一个传感对象, 传感对象包含了两个物体的有关信息, 当物体重叠的时候
调用beginContact ,当物体分开的时候调用endContact
public class ListenerClass implements ContactListener { @Override public void endContact(Contact contact) { } @Override public void beginContact(Contact contact) { } };
有时候, 我们需要得到游戏对象的信息, 怎么办呢?其实这个在我们需要在设置用户数据的时候, 把我们的游戏对象信息设置
到body或者fixture中, 然后取出来, 做逻辑,比如,减血或其他。
好了, Box2D的简单的了解,就到这了。 想深入学习,可以到他的官网查看。
项目下载地址
截屏
http://blog.csdn.net/wu928320442/article/details/17285405