Events and forces with JBox2D and JavaFX

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.

$ Meinungen über "$ s"

  1. Hi eppleton
    It is really cool and excellent to see your pinball program (using JavaFX and JBox2D). I am started using these two to create some basic things like balls and boxes falling. It was OK, but the way the boxes stack are not correct. Some of them are overlapped each other instead of stacking. I think my coordinate transformations between JavaFX Panel and real world should be wrong.
    Can you tell me what is the default world coordinates in JBox2D? Or I would appreciate if you kindly share me your WorldView.java??

    Thanks
    Peter

  2. Pingback: JBox2D and JavaFX: Events and forces | Java Code Geeks

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>