Skip to content

Basic Game Example

Almas Baimagambetov edited this page Jul 22, 2016 · 22 revisions

In this tutorial we are going to create a very basic game. I assume that you have completed Setting up FXGL one way or another and have a Java project in your IDE that has access to the latest version of the FXGL library. If you get lost at any point during the tutorial, there's a link at the end of the page to the full source code.

Pre-coding Stage

First and foremost, let's define some requirements for our simple game:

  1. A 600x600 window.
  2. There is a player on the screen, represented by a blue rectangle.
  3. The user can move the player by pressing W, S, A or D on the keyboard.
  4. UI is represented by a single line of text.
  5. When the player moves, the UI text updates to show how many pixels the player has moved during his lifetime.

At the end of this tutorial you should have something like this: Screenshot

Although it may not sound (or look) like a game, it will help you understand the basic features of FXGL. After you have finished this tutorial, you should be able to build a variety of simple games.

Now that we have a rough idea of what we are expecting from the game, we can go back to the IDE and create a package for our game. (Note: the directory structure is similar to the Maven directory structure, however, if you don't know what this is, don't worry. We will cover the structure at a later stage. At this point having "src" as the main source directory is sufficient.) I'm going to use "com.myname.mygame" as the package name, where myname can optionally be replaced with your username and mygame with the game name.

  1. Create package "com.myname.mygame" in your IDE.
  2. Create a Java class name BasicGameApp in that package.

It is quite common to append "App" to the class where your main() is. This allows other developers to easily identify where the main entry point to your game / application is. Also just to make your next steps a lot easier I suggest that you open your BasicGameApp class and add in these imports:

import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.entity.Entities;
import com.almasb.fxgl.entity.GameEntity;
import com.almasb.fxgl.input.Input;
import com.almasb.fxgl.input.UserAction;
import com.almasb.fxgl.settings.GameSettings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;

We are now ready to do some coding.

Coding Stage

In order to use the FXGL library you need to extend GameApplication and override its abstract methods. The most straightforward way is to make your main class (BasicGameApp that we created) extend it as follows:

public class BasicGameApp extends GameApplication {

    @Override
    protected void initSettings(GameSettings settings) {}

    @Override
    protected void initInput() {}

    @Override
    protected void initAssets() {}

    @Override
    protected void initGame() {}

    @Override
    protected void initPhysics() {}

    @Override
    protected void initUI() {}

    @Override
    protected void onUpdate(double tpf) {}
}

Most IDEs will generate the overridden methods automatically, as soon as you extend GameApplication. Now we want to be able to start the application. To do that simply add the following:

public static void main(String[] args) {
    launch(args);
}

If you've done any JavaFX programming before, then you'll notice that it is the exact same signature that we use to start a JavaFX application. In a nutshell, FXGL is a JavaFX application with game development features, nothing more. At this point you should already be able to run your game, but first let's tweak some settings.

@Override
protected void initSettings(GameSettings settings) {
    settings.setWidth(600);
    settings.setHeight(600);
    settings.setTitle("Basic Game App");
    settings.setVersion("0.1");
    settings.setIntroEnabled(false); // turn off intro
    settings.setMenuEnabled(false);  // turn off menus
}

As you can see all the settings are changed within initSettings(). Once they are set, the settings cannot be changed during runtime (we'll talk about them in more detail some other time). Since this is supposed to be a very basic game, we are going to turn off intro and menus. Ok, you can now click run in your IDE, which should start the game with a 600x600 window and Basic Game App as a title. So we now achieved our requirement 1. Next step is to add a player and show him on the screen. We are going to do this in initGame(). In short, this is where you set up all the stuff that needs to be ready before the game starts.

private GameEntity player;

@Override
protected void initGame() {
    player = Entities.builder()
                     .at(300, 300)
                     .viewFromNode(new Rectangle(25, 25, Color.BLUE))
                     .buildAndAttach(getGameWorld());
}

If you are not familiar with fluent API, then this might be quite a lot to take in one go. So we'll start slowly. There is an instance level field named player of type GameEntity. A game entity is basically a game object. This is everything you need to know about it for now. Entities class contains a collection of convenience static methods to simplify dealing with entities. We first call .builder(), which gives us a new entity builder. By calling .at() we position the entity where we want. In this case it's x = 300, y = 300, so the center of the screen. (Note: a position of an object in FXGL is it's top-left point, akin to the JavaFX convention.) We then tell the builder to create view of the object by using whatever UI node we pass in as the parameter. Here it's a standard JavaFX Rectangle with width = 25, height = 25 and color blue. (Note: you can use any JavaFX node based object, which is pretty cool.) Finally, we call .buildAndAttach() and pass getGameWorld(). The method getGameWorld() simply returns the reference to our game world. By calling build we can obtain the reference to the game entity that we were building. As for the "attach" part, it conveniently allows to attach the built entity straight to the game world. If run the application you should now see a blue rectangle near the center of the screen. Great, we just hit requirement number 2.

We will now proceed with the requirement related to user input. We put the input handling code in initInput().

@Override
protected void initInput() {
    Input input = getInput(); // get input service

    input.addAction(new UserAction("Move Right") {
        @Override
        protected void onAction() {
            player.getPositionComponent().translateX(5); // move right 5 pixels
        }
    }, KeyCode.D);
}

Let's go through this snippet line by line. We first get the input service, alternatively you can just call getInput() directly. Then we add an action, followed by a key code. Again, if you've used JavaFX before then you'll know that these are exactly the same key codes used in event handlers. Anyway, we are basically saying: when 'D' is pressed do the action I've created. Now let's look at the action itself. When we create an action we also give it a name ("Move Right"). It is important as this is fed directly to the controls and menu systems where the user can change them anytime. So the name must be meaningful to the user and also unique. Once we've created the action, we override one of its methods (onAction() this time), and supply some code. That code will be called when the action happens. From the requirements we remember that we want to move the player, so when 'D' is pressed we want to move the player to the right. We call player.getPositionComponent().translateX(5), ignore the component bit for now, it just means we are interested in its position. Then, we translate its X coordinate by 5 pixels. (Note: translate is a terminology used in computer graphics and basically means move.) This results in the player object moving 5 pixels to the right. I think you can guess what the rest of the input code will look like, but just in case here it is for 'W', 'S' and 'A'.

@Override
protected void initInput() {
    Input input = getInput(); // get input service

    input.addAction(new UserAction("Move Right") {
        @Override
        protected void onAction() {
            player.getPositionComponent().translateX(5); // move right 5 pixels
        }
    }, KeyCode.D);

    input.addAction(new UserAction("Move Left") {
        @Override
        protected void onAction() {
            player.getPositionComponent().translateX(-5); // move left 5 pixels
        }
    }, KeyCode.A);

    input.addAction(new UserAction("Move Up") {
        @Override
        protected void onAction() {
            player.getPositionComponent().translateY(-5); // move up 5 pixels
        }
    }, KeyCode.W);

    input.addAction(new UserAction("Move Down") {
        @Override
        protected void onAction() {
            player.getPositionComponent().translateY(5); // move down 5 pixels
        }
    }, KeyCode.S);
}

Requirement 3 - done and dusted. We are more than halfway through, well done!

We now move on to the next bit - UI, which we handle in, you've guessed it, initUI().

@Override
protected void initUI() {
    Text textPixels = new Text();
    textPixels.setTranslateX(50); // x = 50
    textPixels.setTranslateY(100); // y = 100

    getGameScene().addUINode(textPixels); // add to the scene graph
}

For most UI objects we simply use JavaFX objects, since there is no need to re-invent the wheel. The only thing you should note is that when we added a game entity to the world, the game scene picked up the fact that the entity had a view associated with it and so the game scene magically added the entity to the scene graph. With UI objects we are responsible for their additions to the scene graph and we can do so by simply calling getGameScene().addUINode(). That's it for the requirement 4.

In order to complete the last one we are going to use some JavaFX data binding. (Note: data binding in this case refers to view being bound to some data, so that to change the view, we change the data instead. In most cases this is the preferred way and it is very convenient too.) We start by creating an integer property named pixelsMoved:

private IntegerProperty pixelsMoved;

JavaFX properties are much like normal wrapped primitive data types, except they can be bound to other properties and other properties can bind to them. We now initialize it in `initGame()' right after our player creation.

... player creation code
pixelsMoved = new SimpleIntegerProperty();

It is important (for saving/loading systems) that we don't initialize instance level fields on declaration but do it in 'initGame()'. Then we need to let it know how far the player has moved. We can do this in the input handling section.

input.addAction(new UserAction("Move Right") {
    @Override
    protected void onAction() {
        player.getPositionComponent().translateX(5);
        pixelsMoved.set(pixelsMoved.get() + 5);       
    }
}, KeyCode.D);

I'll let you do the same with the rest of movement actions (left, up and down). The last step (both for the requirement and the tutorial) is to bind our UI object to the data object. In initUI() once we've created the textPixels object, we can do the following:

textPixels.textProperty().bind(pixelsMoved.asString());

After that the UI text will show how many pixels the player has moved automatically.

You now have a basic FXGL game. Hopefully you've had fun. Enjoy! Here's the link to the full source code of this tutorial.

Clone this wiki locally