Libgdx中Box2D与Sprite的同步

本文探讨了在游戏开发中如何使用Box2D物理引擎与精灵渲染进行无缝同步,介绍了两种方式及其优缺点,并提出了一种以像素为基准的同步方法,通过自定义类PhysicalObject实现了物理对象与游戏精灵的联动,确保了物理行为与视觉表现的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

box2d中的单位是以米(m)为单位,但我们游戏中的渲染是以像素(px)为单位的。所以就有一个在渲染的时候的同步问题。由于box2d只负责计算数据,所以就有两种方式来进行两者之间的同步。

1.以box2d中的m为基准

2.以精灵的绘制px为基准

libgdx例子中默认的是以box2d的数据作为基准。具体例子参考:https://code.google.com/p/libgdx/source/browse/trunk/tests/gdx-tests/src/com/badlogic/gdx/tests/Box2DTest.java

其中,如注释中所写的,例子建立了一个(48x32)的照相机,假如我们的手机分辨率是480x320,位置(1,1)就相当于手机屏幕的(10,10):

// setup the camera. In Box2D we operate on a
// meter scale, pixels won't do it. So we use
// an orthographic camera with a viewport of
// 48 meters in width and 32 meters in height.
// We also position the camera so that it
// looks at (0,16) (that's where the middle of the
// screen will be located).
camera = new OrthographicCamera(48, 32);
camera.position.set(0, 16, 0);

然后,按照常规方式建立动画精灵。例子中是建立了一块渲染区域(TextureRegion)。

是时候让精灵动起来了,程序在render方法里,对精灵赋予了box2d的一系列同步数据,包括位移,大小,以及旋转角度。

for (int i = 0i < boxes.size(); i++) {
            Body box = boxes.get(i);
            Vector2 position = box.getPosition(); // that's the box's center position
            float angle = MathUtils.radiansToDegrees * box.getAngle(); // the rotation angle around the center
            batch.draw(textureRegion, position.x - 1, position.y - 1, // the bottom left corner of the box, unrotated
                1f, 1f, // the rotation center relative to the bottom left corner of the box
                2, 2, // the width and height of the box
                1, 1, // the scale on the x- and y-axis
                angle); // the rotation angle
}

上面程序是一个什么效果呢?我们也说过,这种方式是以m为单位的,所以,在480x320的机器上1m就相当于10px,我们的程序,画了一个以

((position.x-1)*10,(position.y-1)*10) 为起始点,长度为20px的正方形精灵。当然,我们以正方形的中心为旋转中心,给予精灵一个与box2d相同的旋转角度。


这种方式是可以工作的,也比较容易理解。但是,在实际应用中,会有很多问题。

第一,我们的图像和精灵,通常是以像素px来设计的。我们建立的照相机,也希望以像素px来进行操作。这样就统一了单位,就可以所见即可得的来设计我们的游戏。

第二,对于不规则的box2d图形,旋转中心,大小,角度的同步,以上方法不见得能够非常简洁

第三,每个精灵的操作,都需要这么缩一下,实在是冗余

鉴于多种不方便的操作,以像素为基准的方式应该更形象一些。

所以以像素px为单位的方式建立的照相机貌似如下:

camera = new OrthographicCamera(480, 320);
camera.position.set(240, 160, 0);

精灵,该多大还是多大,我们把box2d的body建立根据精灵来就可以了。

建立一个同步类 PhysicalObject ,建立以下变量:

/**
* the world body , call it directly . its not null 
*/
public Body body ;
/**
* the object such as a sprite, or its a AnimationSprite 
*/
public AdvanceSprite object;
/**
* This is a very important synchronous variables.
* Used to box2d and animation will sync up. At the same time, 
* can draw box2d debugging interface.
*/
public static final float RADIO = 32.00000000f;
/**
* The default restitution of the rigid body
* @see com.badlogic.gdx.physics.box2d.FixtureDef#restitution
*/
public static final float DEFAULT_restitution = 0.3f;
/**
* The default angularDamping of the rigid body
* @see com.badlogic.gdx.physics.box2d.BodyDef#angularDamping
*/
public static final float DEFAULT_angularDamping = (float) (Math.PI / 2);
/**
* The default linerDamping of the rigid body
* @see com.badlogic.gdx.physics.box2d.BodyDef#linearDamping
*/
public static final float DEFAULT_linearDamping = 0.3f;
/**
* The default Friction of the rigid body
* @see com.badlogic.gdx.physics.box2d.FixtureDef#friction
*/
public static final float DEFAULT_friction = 0.4f;
/**
* The default density of the rigid body 
* @see com.badlogic.gdx.physics.box2d.FixtureDef#density
*/
public static final float DEFAULT_density = 1f;
/**
* if the physical object is recycled
*/
private boolean visiable = false;
/**
* scale 
*/
protected float scale = 1.0f;

protected Vector2 offset = new Vector2();

上一段代码:

protected void init(){
        if(null == this.body || null==this.object{
            Gdx.app.error("C2d", "the body and the sprite must be not null");
            System.exit(0);
        }
        this.body.setSleepingAllowed(true);
        
        this.body.setAwake(false);
        this.body.setActive(false);
        
        float shapeHalfWidth = this.object.getWidth()/2;
        float shapeHalfHeight = this.object.getHeight()/2;
        this.scale = this.object.getScaleX();
        
        final MassData massData = this.body.getMassData();
        this.object.setOrigin(-massData.center.x*RADIO+shapeHalfWidth,-massData.center.y*RADIO+shapeHalfHeight);
        offset.set(-massData.center.x*RADIO+shapeHalfWidth,-massData.center.y*RADIO+shapeHalfHeight);
        
        /* move it to the sprite's positon */
        //fix bug: instead of use the (-640,-640) out screen vector . we 
        //use the sprite's position so the when the init method called again and again,
        //its no effect to the whole pysicalObject
        this.body.setTransform(
                new Vector2(this.object.getX(),this.object.getY()).add(offset).mul(1/RADIO), 
                object.getRotation()*MathUtils.degreesToRadians);
    }

设计这个类的初衷,是我们想要以操作精灵的方式来操作PhysicalObject,也就是,我们不管圆形矩形还是不规则多边形,都以它在世界中的左下角,也就是(0,0)坐标来进行绘制,当物体旋转的时候,我们依然可以围绕它的中心进行旋转,当然这个动作的同步是隐藏的。

你可以像操作精灵一样操作物理物体,而不用担心其同步问题。

注意初始化方法里。我们有一个非常重要的变量 offset.它保存了物理body的中心和精灵的位置之间的偏移度,这个偏移度在物理body是不规则图形的时候尤其中哦纲要,在圆形和矩形中,偏移量为0.这个偏移量是以MassData来进行计算的。

接下来是最重要的渲染部分,我们有一个非常重要的同步函数,如下:

private void syncObjectAndBody(){
        final Vector2  position = body.getPosition();
        this.object.setPosition(position.x*PhysicalObject.RADIO -offset.x,position.y*PhysicalObject.RADIO -offset.y);
        this.object.setRotation(MathUtils.radiansToDegrees * this.body.getAngle());
    }

RADIO是为了协调精灵和box2d设置的同步比率,如这里的RADIO=32意思就是1m代表32px。同样的,位置和角度的设定依然是根据物理body的,在计算位置的时候,别忘了加上它的偏移量。

这样,我们就可以用像素px来同步所有的物理操作了。

如下两个函数告诉你怎么根据精灵来得到圆形body的半径和矩形body的高宽。

/** get the the box shape due to the Sprite object */
    public static Vector2 getBoxShapeVector(Sprite object){
        return newVector2(object.getWidth()*object.getScaleX(),object.getHeight()*object.getScaleY()).mul(1/PhysicalObject.RADIO/2);
    }
    /** get the radius of the circle shape due to the Sprite object */
    public static float getCircleShapeRadius(Sprite object){
        return object.getWidth() * object.getScaleX()/PhysicalObject.RADIO/2;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值