2014年7月11日星期五

一个最简单的 libgdx + box2D + tiledMap 模型

yuiyui,终于到了解释这3个东西的时候了,PO主比较笨,分开来会写,结合起来用就懵了。网上教程也没看明白,花了好长时间才初步理解了,唉~ 多年以后回来看会不会感到羞耻呢?

ok,start!



绘制一个球
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void create() {
    ball = new TextureRegion(new Texture(Gdx.files.internal("data/images/ball.png")));
    batch = new SpriteBatch();
}

@Override
public void render() {
    Gdx.gl.glClearColor(.4f, .4f, .4f, .4f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.begin();
    batch.draw(ball, 300400, ball.getRegionWidth(), ball.getRegionHeight());
    batch.end();
}

接着就来到box2D环节

创建一个世界
1
world = new World(new Vector2(0, -9.81f), true);

* -9.81f是标准重力(怎么算的不知道,这么写就对了)

* boolean值是doSleep,大概是没有事情发生的时候会休眠,节省资源吧

创建相机
1
2
camera = new OrthographicCamera();
camera.setToOrtho(false, 1280 / PPM, 720 / PPM);

* false:是否把相机倒过来

* box2D世界的单位是米,因为box2D不能模拟太巨大的世界,(比如重力加速会无效)
我们又需要保持像素不变,所以这里进行一下换算,1280 / PPM, PPM=100;
结果是 1280像素 = 12.8米,后面box2D相关的数字都要除以PPM

为了能看到线框,创建Debug渲染
1
debugRenderer = new Box2DDebugRenderer();

创建2个刚体,一个地面,一个球
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//Static Body 地面
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.type = BodyDef.BodyType.StaticBody;
groundBodyDef.position.set(640 / PPM, 300 / PPM);
//形状
PolygonShape staticBox = new PolygonShape();
staticBox.setAsBox(300 / PPM, 20 / PPM);
//生成
Body bodyG = world.createBody(groundBodyDef);
bodyG.createFixture(staticBox, 0);

//Dynamic Body 球
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(500 / PPM, 600 / PPM);

CircleShape dynamicCircle = new CircleShape();
dynamicCircle.setRadius( 50 / PPM);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = dynamicCircle;
fixtureDef.density = 0.7f;  // 密度
fixtureDef.friction = 0.4f;  // 摩擦
fixtureDef.restitution = 0.8f; // 弹性

body = world.createBody(bodyDef);
body.createFixture(fixtureDef);

* StaticBody可以省略type和FixtureDef,在createFixture的时候是写shape和密度

* BodyDef.position.set 是坐标从起点到圆心的距离

* libgdx的物体x,y值是左下角,box2D是圆心

* setAsBox & settRadius 设置的都是半径

* 确保Body唯一性,否则难以控制

要怎么解释Body、BodyDef、FixtureDef、Shape呢?我苦思冥想终于有了答案,虽然有点H...看图解
修改 render()
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void render() {
    Gdx.gl.glClearColor(.4f, .4f, .4f, .4f);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    world.step(1 / 60f, 62);
    camera.update();
    debugRenderer.render(world, camera.combined);

    batch.begin();
    batch.draw(ball, 150400, ball.getRegionWidth(), ball.getRegionHeight());
    batch.end();
}

* world.step 时步,60帧每秒就设置为 1/60f,后面2个参数是速率迭代 & 位置迭代,效果不明...

* 更新相机,debug绑定世界和相机
这时候ball和刚体球还没有任何联系,下面就是重点

ball只要绑定刚体的坐标和旋转值,它们就能合体了!

修改 batch.draw
1
2
3
4
5
batch.draw(ball, body.getPosition().x * PPM - ball.getRegionWidth() / 2,
           body.getPosition().y * PPM - ball.getRegionWidth() / 2,
           ball.getRegionWidth() / 2, ball.getRegionHeight() / 2,
           ball.getRegionWidth(), ball.getRegionHeight(), 11,
           body.getAngle() * MathUtils.radiansToDegrees);

* ball,X坐标,Y坐标,X旋转中心,Y旋转中心,宽,高,X缩放,Y缩放,旋转值

*  - ball.getRegionWidth() / 2 意思是让坐标回到左下角

在create方法给ball一个加速力, 然后运行
1
body.setLinearVelocity(2, -2);


接下来是地图工具 Tiled map 时间

画2面墙(ground),新建对象层(land),画2个矩形
创建地图用的相机,相当于主角的视角,并载入地图
1
2
3
4
5
mapCam = new OrthographicCamera();
mapCam.setToOrtho(false, 1280720);

map = new TmxMapLoader().load("data/maps/map1.tmx");
renderer = new OrthogonalTiledMapRenderer(map);

在render()添加
1
2
3
mapCam.update();
renderer.setView(mapCam);
renderer.render();
* 此时地图只是个背景,毫无用处

* 为了让ball对地图的墙面有碰撞效果,我们需要绘制2个刚体并定位到墙面上,难点就是如何获取对象层的2个矩形的坐标和宽高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
MapLayer mapLayer = map.getLayers().get("land");
MapObjects objects = map.getLayers().get("land").getObjects();
ArrayList<Float> listW = new ArrayList<Float>();
ArrayList<Float> listH = new ArrayList<Float>();
BodyDef bdef = new BodyDef();
PolygonShape shape = new PolygonShape();
int i = 0;

for(MapObject object : objects) {
    if (object instanceof RectangleMapObject) {
        Rectangle rect = ((RectangleMapObject) object).getRectangle();
        // do something with rect...
        listW.add(rect.getWidth());
        listH.add(rect.getHeight());
        i++;
    }
}

Iterator itW = listW.iterator();
Iterator itH = listH.iterator();

for(MapObject mo : mapLayer.getObjects()) {
    float x = mo.getProperties().get("x", Integer.class) / PPM;
    float y = mo.getProperties().get("y", Integer.class) / PPM;
    if(i - 1 > 0) {
        float w = (Float)itW.next() / 2 / PPM;
        float h = (Float)itH.next() / 2 / PPM;
        shape.setAsBox(w, h);
        bdef.position.set(x + w, y + h);
    }
    world.createBody(bdef).createFixture(shape, 0);
}

* 大意是:获取名为"land"的图层,遍历获取每个形状的宽高,坐标,并生成刚体

* 真实的地图一定不止2块墙面,所以需要用遍历,迭代

不要问我为什么这一大段解释那么少,因为是copy来的

最终效果
再来个图解
地图只负责静态,能动的东西必须要draw和贴图

没有评论:

发表评论