Getting Started with JavaFX Game Programming (for Java Programmers)

Getting Started with JavaFX Game Programming

[i]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
[/i]

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.


[b]I. Basic Setup & Installation[/b]

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.


[b]II. BASIC GRAPHICS[/b]

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.


    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.


              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.


    @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();
    }      


[b]III. Basic Game Loop[/b]

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:


    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.


    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();     
        }
    }


[b]IV. SIMPLE KEYBOARD INPUT[/b]

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 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).


    @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.


    // 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:


    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;
            }
        }
    }

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.

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 :clue:

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.

excellent +1.

my go to thread when I start FX. thanks alot :slight_smile:

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.

Cool that you take the effort, helping others making their first steps in javafx game development.
About gameloops, I use a different approach:

Timeline timeline = new Timeline();
timeline.setCycleCount(Animation.INDEFINITE);
keyframe = new KeyFrame(Duration.millis(1), (ActionEvent event) -> {

        // your gamelogic here

});
timeline.getKeyFrames().add(keyframe);
timeline.play();

The gameloop runs with a delay of 1 ms. Now, if I want to animate objects at a different speed, I use an integer counter, where every count of the integer is 1 ms. Then I use a switch statement to apply gamelogic at every so many counts (img[0] at 50 counts, img[1] at 100 counts etc.). When all the images are animated, the counter is reset to 0 (in the gameloop I only call methods with their own switches, so my gameloop looks quite clean).
Each object gets it’s own counter, ofcourse, unless they animate parallel.
In my current gameproject, I use 8 fullscreen layers in one canvas, where some layers are moving (like slowly moving plants underwater). Then there are some smaller images, like the player and stuff, and ofcourse the interaction with other objects. It runs smooth on a decent gamepc, and when I use something like VisualVM, I can see that the Eden part of the GC hardly fills up, so GC won’t be called that often. For collision detection I use a pitch black mask on top of everything else (like say for walls and stuff), set the Alphachannel to 0 (the layer still retains it’s colorcode) and check for the presence of that black color (in relation to the player) every cycle of the loop.
Works like a charm, and since most of the coding happens outside the loop in reusable methods, I can now focus on creating different scenes (with each it’s own gameloop), where I can tinker where necessary and write code specific to that scene (it’s an adventure game, with some platform elements).

~o~/
I´m just here to express my gratitude for this cute lil tutorial, it´s a good sheet for the very start and just for setting things up , so with that being said ! thank you throws cookies

Since I can’t edit my post, I add a new one.
So… edit [previous post]: I use 1 ms as a pulse for the gameloop, but that’s for faster cpu’s/gpu’s. For the gameloop of Bee vs Mites (part of Bee Aware) I brought it back to 1000 ms / 120, which does not result in lower cpu/gpu load, but does make a difference on a slower pc (my mom’s lol), since the updates occur less. I didn’t like 60 updates per/sec (not smooth enough), but 120 is ok. I’m still a bit confused about this, since JavaFX tries to keep a ‘steady’ framerate of 60 fps, so 60 updates per/sec should have done the trick, still, 120 updates per/sec feels and looks smoother. Elaborating on the framerate: I use a piece of code from stackoverflow to test the framerate, and it seems to ‘wobble’ between 61 and 62 fps.

Anyway, felt I needed to add this… if anyone has more info on creating the perfect gameloop (I still have ‘hickups’, maybe it’s the GC?), I would love to hear that.

Here’s a comparison from a performance graph of a game that I’m developing currently:
AnimationTimer

Imgur

Timeline

Imgur

Timeline is much more spikey than AnimationTimer, so I used the latter, as it’s very smooth.

Cheers

Sorry that didn’t work, can’t edit my posts either:
AnimationTimer:

Timeline:

Given that the issue being discussed is beyond the scope of the article, maybe it would be a good idea to move to a forum thread, where it is easier to edit and make comments and signal appreciation.

We have a board for JavaFX, placed as a child board of Java2D under Game APIs and Engines. Issues pertaining to animation smoothness and alternate ways to structure game loops would certainly be well placed there, and valuable information.

Nice tutorial. I like JavaFX for games, particularly ones that are GUI heavy. The renderer is pretty fast, in my experience. @Shatterhand, your metric confirms my experiences with AnimationTimer vs Timeline.

I am working on a deployment walk-through tutorial, and was thinking I’d use example code in this tutorial to deployed. Part of the point is that we have many of the basic components of a simple game present, and that this would be more interesting (especially for the use of JLINK) than a simple hello-world example.

The following is a slight edit of BasicGraphicsDemo3 in the tutorial above, with the key-listening functionality is attached to the root node.


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.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);
          
        // Attach KeyEvent listening to a Node that has focus.
        root.setFocusTraversable(true);
        root.requestFocus();
        root.setOnKeyPressed(this);
        
        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;
        }
    }
}

just was thinking to mess a little with JavaFX due API2D is to slow if you run the game in mac or ubuntu, and just find this tutorial, I think is great one, but should be good if you teach us how to put all the logic on another class, or using the context object to draw like a canvas. don’t know just suggesting and keep with your awesome work, do you have any youtube channel for this?

Feb. 9, 2019

Things have changed quite a bit! It’s not so much that the basics of JavaFX has changed, but that it seems everything around it has.

The site http://openjfx.io has an excellent “Getting Started” guide with which I was able to switch over to running JavaFX 11 along side OpenJDK 11, using Eclipse. I highly recommend it.

A major hitch now is that in order to run a Java program using JavaFX 11, the good old command “java -jar YourProgram.jar” will no longer work. Instead a command line (as explained in the “Getting Started” guide) that invokes the JavaFX modules will be needed. And the program code base will have to include JavaFX libraries as well.

Creating a program that others will be able to run easily also becomes more complicated, if only because now the user will have to have installed BOTH Java and JavaFX. (Unless, of course, you continue to use a version of Java that includes JavaFX, such as Java 8.)

I’d like to encourage JGO contributors to add tutorials at the following wiki (note that the header page is editable), but any contributions of tutorials or new threads to the site is welcome: