JBox2D and JavaFX: Events and forces
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.