欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

Libgdx专题系列:物理引擎篇 Box2D

程序员文章站 2022-05-15 08:01:48
...

声明: 本系列文章使用的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的简单的了解,就到这了。 想深入学习,可以到他的官网查看。

项目下载地址

截屏

Libgdx专题系列:物理引擎篇 Box2D

http://blog.csdn.net/wu928320442/article/details/17285405