Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (732)
Games in Android Showcase (222)
games submitted by our members
Games in WIP (806)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Getting Started with JavaFX Game Programming (for Java Programmers)  (Read 102608 times)
0 Members and 1 Guest are viewing this topic.
Offline philfrei
« Posted 2016-03-07 03:48:43 »

Getting Started with JavaFX Game Programming

I'm a Java programmer in the process of learning JavaFX, and was surprised by the lack of current tutorials on this subject. Much of what I found online was for an obsolete version of JavaFX, not the JavaFX current in Java 8. The following is meant to help with some of the annoying speed bumps encountered, not to teach game programming. I'm assuming you already know what a game loop is, for example.

Sections that follow:
I. Basic setup and installation (with notes on an Eclipse bug)
II. Basic graphics
III. Basic game loop (AnimationTimer)
IV. Simple Keyboard Input


Intro: Why JavaFX?
  • JavaFX is relatively fast and powerful. As an example, check out this particle generator written by Roland C. "The video shows 28000 particles running at 60 fps in full-hd screen resolution."
  • JavaFX has advantages over AWT/Swing. Oracle has stopped development of AWT/Swing, and is committed to advancing JavaFX as the main GUI for Java, going forward. As a rewrite of Java's GUI, the API is better designed and easier to learn and to code than Swing. While I'm told that not all of Swing's features have been implemented, the vast majority of the functionality has, and JavaFX makes 3D programming and many additional special effects available.
  • JavaFX has some advantages over LWJGL-based graphics systems. The main advantage is that it is easy to integrate widgets (buttons, sliders, etc.) with the 3D graphics. Adding Swing widgets to a LWJGL-based system is difficult. There is also an easier learning curve because the coding, even for 3D, remains "Java-like", whereas with LWJGL one has to get into learning Open-GL and deal with a C-influenced syntax. There are probably legitimate arguments that a system, such as LibGDX will be more powerful, but maybe not by much, as well as back and forth about the comparative advantages of the OpenGL paradigm versus the JavaFX 3D system. But this is something that I am unqualified to address.

I. Basic Setup & Installation

JavaFX now comes standard with Java 8. If you use Java 8 or better, no additional or external libraries need to be imported. I'm not aware of a way to make use of the current JavaFX with earlier Java builds.

As of this writing, Eclipse (and this includes my current installation of NEON) requires an annoying extra step in order to access the JavaFX library. For some reason, the JRE System Library must be removed and reattached before JavaFX will be found. The following steps only have to be done once per project.

After creating a new Java Project:
  • Open the Java Build Path property (right click the Project, select Build Path > Configure Build Path...)
  • Open the Libraries tab, select the JRE System Library and "Remove" it. The system library may be named something like "JRE System Library[JavaSE-1.8]".
  • Click "Add Library...", in the next popup, select "Java System Library" (should be the first choice), and on the next screen "Workspace default JRE (jre[VERSION#])" (should be the last radio button), and "Finish".
If you are making use of another JRE, I'm guessing you will probably know how to alter the above steps to load your preferred JRE System Library. I just use of the default.

At this point, Eclipse should have no problems finding JavaFX objects and methods.


II. BASIC GRAPHICS

The entry class for a JavaFX project has to extend javafx.application.Application. Then, we "launch" the  JavaFX GUI from the main method. The method launch executes the start method. This method provides the programmer with a Stage instance. All of the JavaFX functionality occurs within the context of this Stage instance.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.stage.Stage;

    public class BasicGraphicsDemo extends Application{

        public static void main(String[] args) {

            launch(args);
        }

        @Override
        public void start(Stage stage) throws Exception {

            Group root = new Group();
            Scene scene = new Scene(root, 600, 400);

            stage.setScene(scene);
            stage.show();
        }    
    }


A Stage functions as a top level frame. A Stage can display a Scene. A program may have more than one Scene.

A Scene holds a tree of the type Node. Most of the objects we use in JavaFX are subclasses of Node. By convention, the very first Node of the tree is named root, and is the object Group. A Group is an object that holds a collection of Nodes.

In the above code, the constructor for the Scene includes the root, and the dimension in pixels (width, height) of a display window. There are various other constructors one can use, as can be found in the API for Scene.

The following code adds a blue circle to the display.

1  
2  
3  
4  
5  
6  
7  
              Circle circle = new Circle();
              circle.setCenterX(100);
              circle.setCenterY(200);
              circle.setRadius(40);
              circle.setFill(Color.BLUE);

              root.getChildren().add(circle);


The object Circle is one of several javafx.scene.shape options. Setting attributes is pretty straightforward. Experienced Java programmers should have no problem consulting the API for additional properties and variations.

Adding the Circle node to the root node is a little tricky, but once the pattern is learned, you will find that the same form is consistent over many situations. The collection of nodes held by the Group variable is returned by the method getChildren. Standard practice is to chain the add method onto this collection, as done in the above example. You can consult the API to learn about different forms of adding.

In the next code fragment, a caption is given to the top-level Stage, and the blue Circle is added to the display. When run, the image following this code fragment is displayed.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
    @Override
    public void start(Stage stage) throws Exception {

        stage.setTitle("Basic JavaFX demo");

        Group root = new Group();
        Scene scene = new Scene(root, 600, 400);

        Circle circle = new Circle();
        circle.setCenterX(100);
        circle.setCenterY(200);
        circle.setRadius(40);
        circle.setFill(Color.BLUE);
        root.getChildren().add(circle);
   
        stage.setScene(scene);
        stage.show();
    }      



 

III. Basic Game Loop

JavaFX offers several animation methods, with varying degrees of usefulness for game programming. I initially tried the "Many Balls" animation from "JavaFX for Dummies". This otherwise excellent book (the explanation of Lambdas is the best I've found anywhere) makes use of a method of animation that employs a KeyFrame and a Timeline. The resulting animation is unsatisfactory, as there are noticeable jitters. So, I recommend skipping that and making use of the vastly superior animation method employed in the Particle Generator program cited at the top of this article.

The key Object is named AnimationTimer. The most common practice I've seen is to create an AnimationTimer as in the following:

1  
2  
3  
4  
5  
6  
7  
8  
9  
    AnimationTimer animator = new AnimationTimer()
    {
        @Override
        public void handle(long arg0)
        {
            // update
            // render
        }
    };


The code in the handle method is executed repeatedly when the start method of the AnimationTimer is called, and the repetitions are halted when the stop method is called. The argument arg0 that becomes available to the coder is the clock time at the start of the given repetition, measured in nanoseconds.

I was thrown, initially, by the fact that there is no way to set the frequency or timing of the repetitions. What the JavaFX designers have done is to implement a target repetition rate of 60 frames per second, where a frame corresponds to one iteration of the handle method. If your handle method takes longer than that to execute, then the frame rate slows down accordingly. A new iteration does not start until the current iteration has completed. If your handle method executes more quickly, the system waits rather than running at an fps that is faster than 60.

The following code animates the ball in the previous example, from side to side.

1  
2  
3  
4  
5  
6  
7  
8  
9  
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  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
    import javafx.animation.AnimationTimer;
    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;

    public class BasicGraphicsDemo2 extends Application{

        final int WIDTH = 600;
        final int HEIGHT = 400;
       
        double ballRadius = 40;
        double ballX = 100;
        double ballY = 200;  
        double xSpeed = 4;
   
        public static void main(String[] args) {
   
            launch(args);
        }
       
        @Override
        public void start(Stage stage) throws Exception {

            stage.setTitle("Basic JavaFX demo");

            Group root = new Group();
            Scene scene = new Scene(root, WIDTH, HEIGHT);

            Circle circle = new Circle();
            circle.setCenterX(ballX);
            circle.setCenterY(ballY);
            circle.setRadius(ballRadius);
            circle.setFill(Color.BLUE);
            root.getChildren().add(circle);

            stage.setScene(scene);
            stage.show();

            AnimationTimer animator = new AnimationTimer(){

                @Override
                public void handle(long arg0) {

                    // UPDATE
                    ballX += xSpeed;

                    if (ballX + ballRadius >= WIDTH)
                    {
                        ballX = WIDTH - ballRadius;
                        xSpeed *= -1;
                    }
                    else if (ballX - ballRadius < 0)
                    {
                        ballX = 0 + ballRadius;
                        xSpeed *= -1;
                    }

                    // RENDER
                    circle.setCenterX(ballX);
                }      
            };

            animator.start();    
        }
    }



IV. SIMPLE KEYBOARD INPUT

The JavaFX Event System has many similarities to Java. There are plenty of good and clear examples of usage in the Java Tutorials, JavaFX section. But the tutorial example provided for KeyEvent was overly complicated for my taste. The following is provided as a quick introduction to the basic form.

In this example, EventHandler<KeyEvent> is implemented by the main class (BasicGraphicsDemo3). Alert! When importing KeyEvent, make sure to import from the javafx library (javafx.scene.input.KeyEvent) and not java.awt.event.KeyEvent. I inadvertently did this and wasted a fair bit of time puzzling over the resulting error messages.

A class which implements EventHander must override the method handle(KeyEvent keyEvent).

1  
2  
3  
4  
5  
6  
7  
8  
    @Override
    public void handle(KeyEvent arg0) {

        if (arg0.getCode() == KeyCode.SPACE )
        {
            xSpeed *= -1;
        }
    }


In this example, the KeyEvent variable provided by the event is inspected. If the code matches KeyCode.SPACE, we reverse the direction of the ball. The JavaFX API documents all the available KeyCodes, and includes all the letters, and code names for the arrow keys: KeyCode.UP, KeyCode.DOWN, KeyCode.RIGHT, KeyCode.LEFT.

You can also inspect the API of KeyEvent for other information provided by this variable the event occurs. I don't illustrate more common usages, such as separately tracking pressing and releasing, or handling simultaneous keys. These issues are similar enough to the Java equivalents.

How is the handle method triggered? This is done from a Node, using the method setOnKeyPressed. Other options are also available, e.g., setOnKeyReleased, setOnKeyTyped.  There are two requirements for the Node: it must be part of the Scene that is currently being shown, and, it has to contain the focus.

1  
2  
3  
4  
5  
6  
7  
8  
9  
    // need to attach KeyEvent caller to a Node of some sort.
    // How about an invisible Box? (arbitrary choice)
    final Box keyboardNode = new Box();
    keyboardNode.setFocusTraversable(true);
    keyboardNode.requestFocus();

    keyboardNode.setOnKeyPressed(this); // call to the EventHandler

    root.getChildren().add(keyboardNode);


The choice of Box was totally arbitrary on my part. There is probably a more appropriate subclass of Node to use. The JavaFX tutorial code example uses a StackPane. I suppose I could have also used the existing Circle node for the ball. But it seems more sensible that the keyboard handler be a dedicated Node rather than serve multiple functions.

Giving the node the focus required two steps. First, the node is set to be "FocusTraversable". Once that is done, it is  given the focus with the method requestFocus.

Complete program BasicGraphicsDemo3.java:
1  
2  
3  
4  
5  
6  
7  
8  
9  
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  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
    import javafx.animation.AnimationTimer;
    import javafx.application.Application;
    import javafx.event.EventHandler;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.input.KeyCode;
    import javafx.scene.input.KeyEvent;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Box;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;
     
    public class BasicGraphicsDemo3 extends Application
                  implements EventHandler <KeyEvent>
    {
        final int WIDTH = 600;
        final int HEIGHT = 400;
       
        double ballRadius = 40;
        double ballX = 100;
        double ballY = 200;  
        double xSpeed = 4;
       
        public static void main(String[] args) {
             
            launch(args);
        }
       
        @Override
        public void start(Stage stage) throws Exception {
       
            stage.setTitle("Basic JavaFX demo");
             
            Group root = new Group();
            Scene scene = new Scene(root, WIDTH, HEIGHT);
             
            // Bouncing Ball
            Circle circle = new Circle();
            circle.setCenterX(ballX);
            circle.setCenterY(ballY);
            circle.setRadius(ballRadius);
            circle.setFill(Color.BLUE);
            root.getChildren().add(circle);
             
            // need to attach KeyEvent caller to a Node of some sort.
            // How about an invisible Box?
            final Box keyboardNode = new Box();
            keyboardNode.setFocusTraversable(true);
            keyboardNode.requestFocus();
            keyboardNode.setOnKeyPressed(this);
           
            root.getChildren().add(keyboardNode);
             
            stage.setScene(scene);
            stage.show();
           
            AnimationTimer animator = new AnimationTimer(){
 
                @Override
                public void handle(long arg0) {
       
                    // UPDATE
                    ballX += xSpeed;
                           
                    if (ballX + ballRadius >= WIDTH)
                    {
                        ballX = WIDTH - ballRadius;
                        xSpeed *= -1;
                    } else if (ballX - ballRadius < 0) {
                        ballX = 0 + ballRadius;
                        xSpeed *= -1;
                    }
       
                    // RENDER
                    circle.setCenterX(ballX);
                }
            };
 
            animator.start();
        }
 
        @Override
        public void handle(KeyEvent arg0) {
             
            if (arg0.getCode() == KeyCode.SPACE )
            {
                xSpeed *= -1;
            }
        }
    }

music and music apps: http://adonax.com
Offline philfrei
« Reply #1 - Posted 2016-03-31 18:24:26 »

I just came across what seems like a good recommendation for the Node on which to place a Keyboard "listener." The suggestion is to use the Group node that is at the root of the node tree for the GUI, i.e., the node that is passed as a parameter to the Stage.

This is the practice used in the book "Learn JavaFX 8" by Kishori Sharan, Apress. From what I've seen so far (reading selections on Safari via local library account), this is a very useful book. Unlike many books on JavaFX, this one is receiving high evaluations, mostly 5's and 4's. It goes into depth on the specifics of the GUI and has good coverage of the use of the -fx api for using CSS commands directly to style GUI elements.

I still like "JavaFX for Dummies" for getting started, and for its excellent explanation of lambdas. Of the books I've checked out, these are the only two I recommend so far. There is another book that has separate chapters on using JavaFX for Android and iOS, but on first scan it seems like it could be dangerously sketchy (book has some low evaluations). From folks here at JGO, I'm hearing that the resulting code of the porting tools is not that great, but I haven't had a chance to test this myself and get a sense of just how much is lost in the translation.

music and music apps: http://adonax.com
Offline philfrei
« Reply #2 - Posted 2016-03-31 18:30:11 »

10,000+ views and counting and no comments or feedback. How should I interpret this?

-> Interest exists in the topic, but my writing is not so great or my examples not so useful?

Insecurely yours, Phil  Clueless

music and music apps: http://adonax.com
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline SHC
« Reply #3 - Posted 2016-04-01 06:31:27 »

It's excellent article, but no replies might be due to the formatting, why don't you use headings? Also please remove the HR tags, they just make it mess up.

Offline basil_

« JGO Bitwise Duke »


Medals: 415
Exp: 13 years



« Reply #4 - Posted 2016-04-02 19:34:01 »

excellent +1.

my go to thread when I start FX. thanks alot Smiley
Offline Jedi18

Innocent Bystander





« Reply #5 - Posted 2016-04-13 04:42:31 »

I made this account just to thank you for the tutorial. There are indeed not many tutorials on JavaFX and I'm glad I found yours.
This was a good tutorial and I hope you make more.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 
Sralse (11 views)
2017-07-25 17:13:48

Archive (412 views)
2017-04-27 17:45:51

buddyBro (592 views)
2017-04-05 03:38:00

CopyableCougar4 (1053 views)
2017-03-24 15:39:42

theagentd (1084 views)
2017-03-24 15:32:08

Rule (1056 views)
2017-03-19 12:43:22

Rule (1042 views)
2017-03-19 12:42:17

Rule (1030 views)
2017-03-19 12:36:21

theagentd (1173 views)
2017-03-16 05:07:07

theagentd (1126 views)
2017-03-15 22:37:06
List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05

SF/X Libraries
by SkyAphid
2017-03-02 06:38:56

SF/X Libraries
by SkyAphid
2017-03-02 06:38:32

SF/X Libraries
by SkyAphid
2017-03-02 06:38:05

SF/X Libraries
by SkyAphid
2017-03-02 06:37:51
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!