javafx做游戏之Jbox2d(1)

Jbox2D介绍:

JBox2D是开源的物理引擎Box2D的Java版本,可以直接用于Android。由于JBox2D的图形渲染使用的是Processing库,因此在Android平台上使用JBox2D时,图形渲染工作只能自行开发。该引擎能够根据开发人员设定的参数,如重力、密度、摩擦系数和弹性系数等,自动地进行2D刚体物理运动的全方位模拟。每种物理引擎都有其独特的概念,在学习开源的物理引擎时,首先需要弄明白的就是其基本概念。因此,本节主要为读者复习一下物理学中的一些基本概念,并介绍JBox2D中的一些常用类与概念。

游戏是对真实世界的仿真,其中用到了许多物理学知识,如密度、质量、质心、摩擦力、扭矩以及碰撞(恢复)系数等。接下来,本小节将简要介绍用JBox2D开发游戏时经常用到的一些物理学概念。

密度

物理学中密度指的是单位体积的质量,符号为"ρ",常用单位为kg/m^3。其是物质的一种基本特性,不随物体的质量、体积的改变而改变,同种物质的密度相同。

质量

质量指的是物体中所含物质的量,即物体惯性的大小,国际单位是kg。同一物体的质量通常是一个常量,不因高度、经度或者纬度的改变而变化。但是根据爱因斯坦的相对论,同一物体的质量会随着速度的变化而改变。只有运动接近光速才能感觉到这种变化,因此在游戏中一般不考虑速度对质量的影响。

质心

物体(或物体系)的质量中心,是研究物体(或物体系)机械运动的一个重要参考点。当作用力(或合力)通过该点时,物体只作移动而不发生转动;否则在发生移动的同时物体将绕该点转动。

研究质心的运动时,可将物体的质量看作集中于质心。理论上,质心是对物体的质量分布用"加权平均法"求出的平均中心。

摩擦力

当两个互相接触的物体,如果要发生或者已经发生相对运动。就会在接触面上产生一种阻碍该相对运动的力,这种力就称之为摩擦力。其基本情况如下图所示。

提示 根据物体是否发生相对运动可以分为静摩擦力与滑动摩擦力,实际开发中可以进行简化,但若要模拟更加真实的效果就需要分别开发。

扭矩

扭矩在物理学中就是力矩的大小,等于力与力臂的乘积,国际单位是Nm(牛米)。在力臂不变的情况下,力越大,扭矩越大。基本情况如下图所示。

恢复系数

两物体碰撞后的总动能与碰撞前的总动能之间的比称之为恢复系数,其取值范围为0~1。如果恢复系数为 1,则碰撞为完全弹性碰撞,满足机械能守恒;如果恢复系数小于1并且大于0,则为非完全弹性碰撞,不满足机械能守恒,这种情况是最常见的;如果恢复系数为0,则为完全非弹性碰撞,两个物体会粘在一起。

看效果图:

javafx和jbox2d组合:

1.坐标系对应

在真实物理环境中坐标系为向右x轴为正方向,向上为y轴正方向。如下图:

在fx界面中向右x轴为正方向,向下为y轴正方向。如下图:

从图中可以看出,两个坐标系中x轴方向完全一致,但y轴方向完全相反,因此需要做坐标变换。

2.类设计(jbox2d版本为2.0.1)

PhysicalObject类设计:

这是物理对象实现的基类,包含World和Body两个重要对象属性

World对象是表示物理世界环境,Body对象表示物理世界中的刚体。

核心代码如下:

public abstract class PhysicalObject extends Parent {

    protected World world;
    private boolean shouldCreate = true;
    private boolean shouldDestroy = false;
    private boolean destroyed = false;

    protected Body body;

    public abstract void createPhysicsObject();

    public abstract void update();

    public void createPhysicsObject(World world) {
        this.world = world;
        createPhysicsObject();
    } 
}

 

Ball类设计:

Ball类继承PhysicalObject 对象,具体代码如下:

注意createPhysicsObject方法,因为球是原形,所以使用CircleDef 来描述球

public class Ball extends PhysicalObject {
    public int radius = 3;
    private DoubleProperty xPos = new SimpleDoubleProperty(50);
    private DoubleProperty yPos = new SimpleDoubleProperty(6);
    private DoubleProperty angle = new SimpleDoubleProperty(0);
    private Image ball = new Image(this.getClass().getResourceAsStream(
            "Ball.png"));
    public ImageView iv = new ImageView(ball);;
    private Timeline endAnimator;

    public Ball(World world, float xPos, float yPos) {
        this.world = world;
        this.xPos.set(xPos);
        this.yPos.set(yPos);
        init();
        create();
    }

    private void init() {
        endAnimator = new Timeline();
        KeyFrame frame1 = new KeyFrame(Duration.millis(200), new KeyValue(
                iv.opacityProperty(), 0.1, Interpolator.LINEAR), new KeyValue(
                iv.translateYProperty(), -30, Interpolator.LINEAR));

        KeyFrame frame2 = new KeyFrame(Duration.millis(250),
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent event) {
                        setDestroyed(true);
                    }
                });
        endAnimator.getKeyFrames().addAll(frame1, frame2);
    }

    private void create() {
        iv.xProperty().bind(xPos.multiply(10).subtract(ball.getWidth() / 2));
        iv.yProperty().bind(yPos.multiply(10).subtract(ball.getHeight() / 2));
        iv.rotateProperty().bind(angle);
        getChildren().add(iv);
    }

    @Override
    public void createPhysicsObject() {
        CircleDef cd = new CircleDef();
        cd.radius = radius;
        cd.density = 5.0f;
        cd.friction = 0.2f;
        cd.restitution = 0.75f;
        BodyDef bd = new BodyDef();
        bd.position.set(new Vec2(xPos.floatValue(), yPos.floatValue()));
        body = world.createBody(bd);
        body.createShape(cd);
        body.setMassFromShapes();
        body.setUserData(this);

    }

    @Override
    public void markDestroyed() {
        super.markDestroyed();
        endAnimator.playFromStart();
    }

    @Override
    public void update() {
        if (isShouldDestroy()) {
            if (body != null) {
                world.destroyBody(body);
                body = null;
            }
            return;
        }
        xPos.set(body.getPosition().x);
        yPos.set(body.getPosition().y);
        angle.set(Math.toDegrees(body.getAngle()));
    }

}

 

Bridge类设计:

Bridge类继承PhysicalObject 对象,具体代码如下

注意:RevoluteJointDef为旋转关节
一个旋转关节会强制两个物体共享一个锚点,即所谓铰接点。旋转关节只有一个自由度:两个物体的相对旋转。

需深入理解可以参考详细教程

public class Bridge extends PhysicalObject {

    private DoubleProperty xPos = new SimpleDoubleProperty(50);
    private DoubleProperty yPos = new SimpleDoubleProperty(6);
    private int width = 10;
    private Image PLANK_IMAGE = new Image(this.getClass().getResourceAsStream(
            "BridgePiece.png"));

    public ImageView iv;

    private Timeline endAnimator;

    public Bridge(World world, float xPos, float yPos, int width) {
        this.world = world;
        this.xPos.set(xPos);
        this.yPos.set(yPos);
        this.width = width;
        create();
    }

    private void create() {
    }

    @Override
    public void createPhysicsObject() {
        float pw = 1.2f;
        PolygonDef sd = new PolygonDef();
        sd.setAsBox(pw / 2f, 0.2f);
        sd.density = 60f;
        sd.friction = 0.2f;
        int numPlanks = width;

        RevoluteJointDef jd = new RevoluteJointDef();
        Body prevBody = null;
        Body firstBody = null;
        Vec2 anchor = null;
        for (int i = 0; i < numPlanks; i++) {
            BodyDef bd = new BodyDef();
            bd.position.set(xPos.floatValue() + 1.5f * i, yPos.floatValue());
            Body body = world.createBody(bd);
            body.createShape(sd);
            getChildren().add(new BridgePiece(PLANK_IMAGE, body));
            if (prevBody == null) {
                firstBody = body;
            } else {
                if (i != numPlanks - 1) {
                    body.setMassFromShapes();
                }
                anchor = new Vec2(xPos.floatValue() - pw + 1.5f * i,
                        yPos.floatValue());
                jd.initialize(prevBody, body, anchor);
                world.createJoint(jd);
            }
            prevBody = body;
        }
        Vec2 anchor1 = new Vec2(xPos.floatValue() - pw + 1.5f * numPlanks,
                yPos.floatValue());
        jd.initialize(prevBody, firstBody, anchor1);
        world.createJoint(jd);
    }

    @Override
    public void markDestroyed() {
        super.markDestroyed();
        endAnimator.playFromStart();
    }

    @Override
    public void update() {
        for (Node c : getChildren()) {
            ((BridgePiece) c).update();
        }
    }

}

 

下载...

更多源码下载...

转载于:https://www.cnblogs.com/javafx/archive/2012/11/29/2794524.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值