7.Libgdx扩展学习之Box2D_距离关节 旋转关节

文章中的概念来自《Box2D》中文手册

上一节中我们介绍了关节和鼠标关节的使用方法,本章中我们继续介绍距离关节和旋转关节

距离关节(DistanceJoint)

距离关节是两个物体上各有一点,两点之间的距离必须固定不变。当指定一个距离关节时,两个物体必须已在应有的位置上。之后指定世界坐标中的两个锚点。第一个锚点连接到物体1,第二个锚点连接到物体2。这两点隐含距离约束的长度。
7.Libgdx扩展学习之Box2D_距离关节 旋转关节_第1张图片

// Define the distance joint
DistanceJointDef distanceJointDef = new DistanceJointDef();
// 距离关节连接的2个Body
distanceJointDef.bodyA=smallBall;
distanceJointDef.bodyB=bigBall;
// 是否允许两个Body碰撞
distanceJointDef.collideConnected=false;
// 两个Body之间的距离
distanceJointDef.length = 2.0f;

关节可以具有弹性,通过定义2个常数:频率(frequency)和阻尼率(damping ratio)。频率影响震动的快慢,典型情况下频率要小于时间步的一半。比如每秒执行60次时间步,距离关节的频率就要小于30。
阻尼率无单位,取值在「0,1」之间。当阻尼率设置为1时,没有振动。

        // 下面2个参数使关节具有弹性
        distanceJointDef.dampingRatio = 0.4f;
        distanceJointDef.frequencyHz = 4.0f;

下面是测代码
7.Libgdx扩展学习之Box2D_距离关节 旋转关节_第2张图片

/**
 * 距离关节
 */
public class DistanceJointTest extends ApplicationAdapter {

    World world;
    Box2DDebugRenderer box2DDebugRenderer;
    Body hitBody, groundBody;

    OrthographicCamera camera;
    Vector3 point = new Vector3();

    float scene_width = 12.8f;
    float scene_height = 7.2f;

    QueryCallback callback = new QueryCallback() {
        @Override
        public boolean reportFixture(Fixture fixture) {
            if (fixture.testPoint(point.x, point.y)) {
                hitBody = fixture.getBody();
                return  false;
            } else
                return true;
        }
    };

    @Override
    public void create() {
        world = new World(new Vector2(0.0f, -9.8f), true);
        box2DDebugRenderer = new Box2DDebugRenderer();

        camera = new OrthographicCamera(scene_width, scene_height);
        camera.position.set(scene_width / 2, scene_height / 2, 0);
        camera.update();

        groundBody = createGroundWall();

        Gdx.input.setInputProcessor(new HandA());

        createDistanceJoint();

    }

    @Override
    public void render() {
        world.step( 1/ 60f, 6, 2);

        Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        box2DDebugRenderer.render(world, camera.combined);
    }

    @Override
    public void dispose() {
        world.dispose();
        box2DDebugRenderer.dispose();
    }

    public void createDistanceJoint() {
        Body smallBall = createSphere(BodyDef.BodyType.DynamicBody, 0f, 3.75f, .8f, .8f, .4f, .25f);
        Body bigBall = createSphere(BodyDef.BodyType.DynamicBody, 3.0f, 4.5f, .8f, 1f, .4f, .5f);

        // Define the distance joint
        DistanceJointDef distanceJointDef = new DistanceJointDef();
        distanceJointDef.bodyA=smallBall;
        distanceJointDef.bodyB=bigBall;
        distanceJointDef.collideConnected=false;
        distanceJointDef.length = 5.0f;
        // 下面2个参数使关节具有弹性
        distanceJointDef.dampingRatio = 0.4f;
        distanceJointDef.frequencyHz = 4.0f;
        distanceJointDef.localAnchorA.set(0,0);
        distanceJointDef.localAnchorB.set(0,0);

        world.createJoint(distanceJointDef);
    }

    private Body createSphere(BodyDef.BodyType type, float x, float y, float d, float r, float f, float radius) {
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = type;
        bodyDef.position.set(scene_width * 0.5f+x,y);
        bodyDef.angle=0;
        Body ball = world.createBody(bodyDef);

        FixtureDef fixtureDef=new FixtureDef();
        fixtureDef.density=d;
        fixtureDef.restitution=r;
        fixtureDef.friction=f;
        fixtureDef.shape=new CircleShape();
        fixtureDef.shape.setRadius(radius);

        ball.createFixture(fixtureDef);
        fixtureDef.shape.dispose();

        return ball;
    }

    public Body createGroundWall() {
        BodyDef bodyDef = new BodyDef();
        bodyDef.position.set(scene_width * 0.5f, 0.2f);
        Body body1 = world.createBody(bodyDef);

        PolygonShape polygonShape = new PolygonShape();
        polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
        body1.createFixture(polygonShape, 0.0f);

        bodyDef.position.set(0.4f, scene_height * 0.5f);
        Body body2 = world.createBody(bodyDef);

        polygonShape.setAsBox(0.2f, scene_height * 0.5f);
        body2.createFixture(polygonShape, 0);

        bodyDef.position.set(12.4f, scene_height * 0.5f);
        Body body3 = world.createBody(bodyDef);

        polygonShape.setAsBox(0.2f, scene_height * 0.5f);
        body3.createFixture(polygonShape, 0);

        bodyDef.position.set(scene_width * 0.5f, 7.0f);
        Body body4 = world.createBody(bodyDef);

        polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
        body4.createFixture(polygonShape, 0);
        polygonShape.dispose();

        return body1;
    }

    class HandA extends InputAdapter {
        MouseJoint mouseJoint;
        Vector2 target = new Vector2();

        @Override
        public boolean touchDragged(int screenX, int screenY, int pointer) {
            if (mouseJoint != null) {
                camera.unproject(point.set(screenX, screenY, 0));
                mouseJoint.setTarget(target.set(point.x, point.y));
            }

            return false;
        }

        @Override
        public boolean touchDown(int screenX, int screenY, int pointer, int button) {
            camera.unproject(point.set(screenX, screenY, 0));
            hitBody = null;
            world.QueryAABB(callback, point.x - 0.0001f, point.y - 0.0001f, point.x + 0.0001f, point.y + 0.0001f);

            if (hitBody == null || hitBody.equals(groundBody)) return false;

                MouseJointDef mouseJointDef = new MouseJointDef();
                mouseJointDef.bodyA = groundBody;
                mouseJointDef.bodyB = hitBody;
                mouseJointDef.collideConnected = true;
                mouseJointDef.target.set(point.x, point.y);
                mouseJointDef.maxForce = 1000.0f * hitBody.getMass();

                mouseJoint = (MouseJoint) world.createJoint(mouseJointDef);
                hitBody.setAwake(true);

            return false;
        }

        @Override
        public boolean touchUp(int screenX, int screenY, int pointer, int button) {
            // 鼠标关节,不再使用时要销毁
            if (mouseJoint != null) {
                world.destroyJoint(mouseJoint);
                mouseJoint = null;
            }
            return false;
        }
    }
}

旋转关节(RevoluteJoint)

旋转关节会强制两个物体公用一个锚点。旋转关节只有一个自由度:两个物体相对旋转。这称之为关节角。
要指定一个旋转关节,需要提供两个物体以及世界坐标的一个锚点,可以参考下面定义:
7.Libgdx扩展学习之Box2D_距离关节 旋转关节_第3张图片

// Define the revolute joint
RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
revoluteJointDef.bodyA=smallBall;
revoluteJointDef.bodyB=bigBall;
revoluteJointDef.collideConnected=false;
// 指定锚点
revoluteJointDef.localAnchorA.set(0,0);
revoluteJointDef.localAnchorB.set(-2.0f,0);

在Box2D中默认是逆时针旋转的,此时关节角为正,而且旋转角也是弧度制的。在创建两个物体时物理当前的角度是怎样的,旋转关节角都为0。

每次执行step后,可以更新马达的参数。这样可以实现有些有趣的功能。可以在每个时间步中更新关节速度,使得它像正炫波或者任意一个想要的函数那样前后摆动
7.Libgdx扩展学习之Box2D_距离关节 旋转关节_第4张图片

/**
 * 旋转关节
 */
public class RevoluteJointTest extends ApplicationAdapter {
    World world;
    Box2DDebugRenderer box2DDebugRenderer;
    Body hitBody, groundBody;

    OrthographicCamera camera;
    Vector3 point = new Vector3();

    float scene_width = 12.8f;
    float scene_height = 7.2f;

    QueryCallback callback = new QueryCallback() {
        @Override
        public boolean reportFixture(Fixture fixture) {
            if (fixture.testPoint(point.x, point.y)) {
                hitBody = fixture.getBody();
                return  false;
            } else
                return true;
        }
    };

    @Override
    public void create() {
        world = new World(new Vector2(0.0f, -9.8f), true);
        box2DDebugRenderer = new Box2DDebugRenderer();

        camera = new OrthographicCamera(scene_width, scene_height);
        camera.position.set(scene_width / 2, scene_height / 2, 0);
        camera.update();

        groundBody = createGroundWall();

        Gdx.input.setInputProcessor(new HandA());

        createRevoluteJoin();

    }

    @Override
    public void render() {
        world.step( 1/ 60f, 6, 2);

        Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        box2DDebugRenderer.render(world, camera.combined);
    }

    @Override
    public void dispose() {
        world.dispose();
        box2DDebugRenderer.dispose();
    }

    public void createRevoluteJoin() {
        // 第一个Body要设置为Static才能保证第二个Body围绕第一个旋转
        Body smallBall = createSphere(BodyDef.BodyType.StaticBody, 0f, 3.75f, 1f, 1f, 0f, .25f);
        Body bigBall = createSphere(BodyDef.BodyType.DynamicBody, 0f, 3.75f, 1f, 1f, 0f, .5f);

        // Define the revolute joint
        RevoluteJointDef revoluteJointDef = new RevoluteJointDef();
        revoluteJointDef.bodyA=smallBall;
        revoluteJointDef.bodyB=bigBall;
        revoluteJointDef.collideConnected=false;
        revoluteJointDef.localAnchorA.set(0,0);
        revoluteJointDef.localAnchorB.set(-2.0f,0);
        revoluteJointDef.enableMotor=true;
        revoluteJointDef.maxMotorTorque=360;
        revoluteJointDef.motorSpeed=100f* MathUtils.degreesToRadians;

        world.createJoint(revoluteJointDef);
    }

    private Body createSphere(BodyDef.BodyType type, float x, float y, float d, float r, float f, float radius) {
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = type;
        bodyDef.position.set(scene_width * 0.5f+x,y);
        bodyDef.angle=0;
        Body ball = world.createBody(bodyDef);

        FixtureDef fixtureDef=new FixtureDef();
        fixtureDef.density=d;
        fixtureDef.restitution=r;
        fixtureDef.friction=f;
        fixtureDef.shape=new CircleShape();
        fixtureDef.shape.setRadius(radius);

        ball.createFixture(fixtureDef);
        fixtureDef.shape.dispose();

        return ball;
    }

    public Body createGroundWall() {
        BodyDef bodyDef = new BodyDef();
        bodyDef.position.set(scene_width * 0.5f, 0.2f);
        Body body1 = world.createBody(bodyDef);

        PolygonShape polygonShape = new PolygonShape();
        polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
        body1.createFixture(polygonShape, 0.0f);

        bodyDef.position.set(0.4f, scene_height * 0.5f);
        Body body2 = world.createBody(bodyDef);

        polygonShape.setAsBox(0.2f, scene_height * 0.5f);
        body2.createFixture(polygonShape, 0);

        bodyDef.position.set(12.4f, scene_height * 0.5f);
        Body body3 = world.createBody(bodyDef);

        polygonShape.setAsBox(0.2f, scene_height * 0.5f);
        body3.createFixture(polygonShape, 0);

        bodyDef.position.set(scene_width * 0.5f, 7.0f);
        Body body4 = world.createBody(bodyDef);

        polygonShape.setAsBox(scene_width * 0.5f, 0.2f);
        body4.createFixture(polygonShape, 0);
        polygonShape.dispose();

        return body1;
    }

    class HandA extends InputAdapter {
        MouseJoint mouseJoint;
        Vector2 target = new Vector2();

        @Override
        public boolean touchDragged(int screenX, int screenY, int pointer) {
            if (mouseJoint != null) {
                camera.unproject(point.set(screenX, screenY, 0));
                mouseJoint.setTarget(target.set(point.x, point.y));
            }

            return false;
        }

        @Override
        public boolean touchDown(int screenX, int screenY, int pointer, int button) {
            camera.unproject(point.set(screenX, screenY, 0));
            hitBody = null;
            world.QueryAABB(callback, point.x - 0.0001f, point.y - 0.0001f, point.x + 0.0001f, point.y + 0.0001f);

            if (hitBody == null || hitBody.equals(groundBody)) return false;

            MouseJointDef mouseJointDef = new MouseJointDef();
            mouseJointDef.bodyA = groundBody;
            mouseJointDef.bodyB = hitBody;
            mouseJointDef.collideConnected = true;
            mouseJointDef.target.set(point.x, point.y);
            mouseJointDef.maxForce = 1000.0f * hitBody.getMass();

            mouseJoint = (MouseJoint) world.createJoint(mouseJointDef);
            hitBody.setAwake(true);

            return false;
        }

        @Override
        public boolean touchUp(int screenX, int screenY, int pointer, int button) {
            // 鼠标关节,不再使用时要销毁
            if (mouseJoint != null) {
                world.destroyJoint(mouseJoint);
                mouseJoint = null;
            }
            return false;
        }
    }
}

你可能感兴趣的:(Libgdx,Libgdx扩展学习,box2d)