Статьи

JBox2D и JavaFX: события и силы

В вчерашних примерах вы видели, как можно создать простой мир, отображать его с помощью WorldView и как создавать собственные средства визуализации. Теперь мы собираемся добавить пользовательский ввод. Мы создадим элемент управления, который ведет себя как флиппер в автомате для игры в пинбол.

Для этого мы создадим Джойнт. В JBox2D суставы используются для привязки тел к миру или друг к другу. Мы создадим статическое круглое тело, которое будет служить осью для нашего флиппера, и свяжем с ним ящик с помощью RevoluteJoint.

Чтобы упростить код, мы сначала определим базовый класс JointBuilder и RevoluteJointBuilder:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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);
 }
}

А вот RevoluteJointBuilder:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;
 }
 
}

Теперь мы можем изменить наш HelloWorld-пример следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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();
 }
}

Это отобразит нашу сцену, и вы увидите, как сустав предотвращает падение динамического бокса на землю и как он ограничивает его движение.

Следующий шаг — позволить пользователю управлять им. Для этого мы будем применять силу, когда пользователь нажимает клавишу. Добавьте это после создания экземпляра сцены:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
 }
 
}
 });

Это все на данный момент. В следующих частях этого урока мы сделаем немного больше пользовательского рендеринга и создадим несколько хороших пользовательских узлов.

Ссылка: События и силы с JBox2D и JavaFX от нашего партнера JCG Тони Эппла в блоге Eppleton .