Desktop Java

JBox2D and JavaFX: Events and forces

In yesterdays samples you saw how you can create a simple world, and display it with WorldView, and how to provide custom Renderers. Now we’re going to add some user input. We’ll create a control that behaves like a flipper in a pinball machine.

To do that we’ll create a Joint. In JBox2D Joints are used to constrain bodies to the world or to each other. We’ll create a static circular Body that will serve as the axis for our flipper, and bind a Box to it via a RevoluteJoint.

To simplify the code, we’ll first define a JointBuilder base class and a RevoluteJointBuilder:

public abstract class JointBuilder, T extends JointDef> {

protected World world;
 protected T jointDef;

protected JointBuilder(World world, T jointDef) {
 this.world = world;
 this.jointDef = jointDef;
 }

public K bodyA(Body a) {
 jointDef.bodyA = a;
 return (K) this;
 }

public K bodyB(Body b) {
 jointDef.bodyB = b;
 return (K) this;
 }

public K userData(Object userData) {
 jointDef.userData = userData;
 return (K) this;
 }

public K type(JointType type) {
 jointDef.type = type;
 return (K) this;
 }

public K collideConnected(boolean coco) {
 jointDef.collideConnected = coco;
 return (K) this;
 }

public Joint build() {
 return world.createJoint(jointDef);
 }
}

And here’s the RevoluteJointBuilder:

public class RevoluteJointBuilder extends JointBuilder {

public RevoluteJointBuilder(World world, Body a, Body b, Vec2 anchor) {
 super(world, new RevoluteJointDef());
 jointDef.initialize(a, b, anchor);
 }

public RevoluteJointBuilder enableLimit(boolean enable) {
 jointDef.enableLimit = enable;
 return this;
 }

public RevoluteJointBuilder enableMotor(boolean motor) {
 jointDef.enableMotor = motor;
 return this;
 }

public RevoluteJointBuilder localAnchorA(Vec2 localAnchorA) {
 jointDef.localAnchorA = localAnchorA;
 return this;
 }

public RevoluteJointBuilder localAnchorB(Vec2 localAnchorB) {
 jointDef.localAnchorB = localAnchorB;
 return this;
 }

public RevoluteJointBuilder lowerAngle(float lowerAngle) {
 jointDef.lowerAngle = lowerAngle;
 return this;
 }

public RevoluteJointBuilder maxMotorTorque(float maxMotorTorque) {
 jointDef.maxMotorTorque = maxMotorTorque;
 return this;
 }

public RevoluteJointBuilder motorSpeed(float motorSpeed) {
 jointDef.motorSpeed = motorSpeed;
 return this;
 }

public RevoluteJointBuilder referenceAngle(float referenceAngle) {
 jointDef.referenceAngle = referenceAngle;
 return this;
 }

public RevoluteJointBuilder upperAngle(float upperAngle) {
 jointDef.upperAngle = upperAngle;
 return this;
 }

}

Now we can modify our HelloWorld-Example like this:

public class HelloWorld extends Application {

public static void main(String[] args) {
 Application.launch(args);
 }

@Override
 public void start(Stage primaryStage) {
 World world = new World(new Vec2(0, -2f), true);
 primaryStage.setTitle("Hello World!");
 NodeManager.addCircleProvider(new MyNodeProvider());

new CircleBuilder(world).userData("ball").position(0.1f, 4).type(BodyType.DYNAMIC).restitution(1).density(2).radius(.15f).friction(.3f).build();
 final Body flipperBody = new BoxBuilder(world).position(0, 2).type(BodyType.DYNAMIC).halfHeight(.02f).halfWidth(.2f).density(2).friction(0).userData("flipper").build();
 Vec2 axis = flipperBody.getWorldCenter().add(new Vec2(.21f, 0));
 Body axisBody = new CircleBuilder(world).position(axis).type(BodyType.STATIC).build();
 new RevoluteJointBuilder(world, flipperBody, axisBody, axis).upperAngle(.6f).lowerAngle(-.6f)
            .enableMotor(true).enableLimit(true).maxMotorTorque(10f).motorSpeed(0f).build();

 Scene scene = new Scene(new WorldView(world, 200, 400, 50), 500, 600);

// ground
 new BoxBuilder(world).position(0, -1f).halfHeight(1).halfWidth(5).build();
 primaryStage.setScene(scene
 );
 primaryStage.show();
 }
}

This will display our scene and you’ll see how the Joint prevents the dynamic Box from falling to the ground and how it constrains it’s movement.

The next step is to allow the user to control it. For this we’ll apply a force when the user presses a key. Add this after the instantiation of the scene:

scene.setOnKeyPressed(new EventHandler() {

@Override
 public void handle(KeyEvent ke) {

if (ke.getCode()
 == KeyCode.LEFT) {
 flipperBody.applyTorque(-15f);
 }

}
 });

scene.setOnKeyReleased(new EventHandler() {

@Override
 public void handle(KeyEvent ke) {

if (ke.getCode()
 == KeyCode.LEFT) {
 flipperBody.applyTorque(15f);
 }

}
 });

That’s it for now. In the next parts of this tutorial we’ll do a bit more custom rendering and create some nice custom Nodes.

Reference: Events and forces with JBox2D and JavaFX from our JCG partner Toni Epple at the Eppleton blog.

Toni Epple

Anton is a consultant worldwide for a wide variety of companies, ranging from startups to Fortune 500 companies, in many areas, including finance institutions and aerospace. His main interest is Client side development, and he has authored books and numerous articles on this topic. He is a member of the NetBeans Dream Team and a Oracle Java Champion. In 2013 he was elected as a JavaONE Rockstar, in 2014 he received a Duke’s Choice Award for his work on DukeScript.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button