Build a Brick Breaking Video Game

By Pavel Porvatov, September 22, 2008

JavaFX technology enables you to easily write a full-featured game with rich graphics for desktop and mobile platforms. You can use the same code for both platforms and still have very compact code.

Understanding the Code

All of the sample's code is split into several classes that represent all entities of the game: the Brick, Ball, Bat, and Bonus classes. The more complex classes are Splash and Level.

Code Details

The Main class contains the entry point of the game. The game starts from the run method, as shown in Figure 1.

Source Code
function run(__ARGS__ : String[]) {
    // Initialization should be the first
    Config.initialize(IS_MOBILE);

    mainFrame = MainFrame {
        title: "Brick Breaker"
        resizable: false

        scene: Scene {
            fill: Color.BLACK
            width: Config.screenWidth
            height: Config.screenHeight
        }
    }
}

Figure 1: Main.run Method

The run method accomplishes two objectives. The first objective is the initialization of the Config class (more about this class later). The second objective is the creation of the MainFrame class, which represents a frame of the game with the specified title, width, height, and other properties. While a user plays, the game content of the MainFrame.scene is replaced by the Splash class or the Level class. The MainFrame class also contains public game properties such as the following:

  • lifeCount - Number of remaining lives
  • score - Current scores

The Config class contains many constants and all the images of the game. The Config.initialize method should be invoked once initially, as Figure 1 shows. This method loads all images and prepares all constants of the game. The single parameter identifies the targeted system. On mobile phones, a small screen is used with small images. For the desktop version, large images with a big screen resolution are used.

The Ball class represents the ball in the game, as shown in Figure 2.

Source Code
// Default size of the ball
public def DEFAULT_SIZE = 2;

// Maximum size of the ball
public def MAX_SIZE = 5;

public class Ball extends CustomNode {
    // Size of the ball
    public var size = DEFAULT_SIZE on replace {
        if (imageView == null) {
            imageView = ImageView {};
        }

        imageView.image = Config.images[Config.IMAGE_BALL_0 + size];
        diameter = imageView.image.width - Config.shadowWidth as Integer;
    };

    // Diameter of the ball (without shadow)
    public-read var diameter;

    var imageView: ImageView;

    override public function create(): Node {
        imageView
    }
}

Figure 2: Ball Class

In this game, the ball can have several sizes, which explains why the size property was introduced. The values of this property lie between 0 and MAX_SIZE. The read-only diameter property reflects the diameter of the ball as it corresponds to the current size.

The Bat class represents the bat, as shown in Figure 3. Like the ball, it can have different sizes and it can be manipulated by the size property. To obtain the current width of the bat, use the width property.

Source Code
// Default size of the bat
public def DEFAULT_SIZE = 2;

// Maximum size of the bat
public def MAX_SIZE = 7;

public class Bat extends CustomNode {
    // Size of the bat
    public var size = DEFAULT_SIZE;

    // Width of the bat (without shadow)
    public-read var width: Integer = bind (size * 12 + 45) * Config.scale as Integer;

    // Height of the bat (without shadow)
    public-read var height;

    ...

Figure 3: Bat Class

Many kinds of bricks are featured in the game. All bricks are represented by the Brick class, as shown in Figure 4. The type property is a type of the brick that defines the brick visual representation and some additional characteristics. The type property can have values only from the list of constants, TYPE_.... Some bricks need to be beaten several times before they break, while other bricks cannot be broken at all.

Source Code
// Types of bricks
public def TYPE_BLUE = 0;
public def TYPE_BROKEN1 = 1;
public def TYPE_BROKEN2 = 2;
public def TYPE_BROWN = 3;
public def TYPE_CYAN = 4;
public def TYPE_GREEN = 5;
public def TYPE_GREY = 6;
public def TYPE_MAGENTA = 7;
public def TYPE_ORANGE = 8;
public def TYPE_RED = 9;
public def TYPE_VIOLET = 10;
public def TYPE_WHITE = 11;
public def TYPE_YELLOW = 12;

public class Brick extends CustomNode {
    // Type of the brick
    public-init var type: Integer;

    // Invoked when the ball kicks the brick.
    // Returns true if the brick was broken or false otherwise
    public function kick(): Boolean {

    ...

Figure 4: Brick Class

After an arbitrary brick is broken, it can produce a bonus that falls. A player can catch this bonus and gain an advantage, such as an extra life. The Bonus class in Figure 5 represents all game bonuses. The type property of the Bonus class contains the type of the bonus. It can be assigned one of the values from the TYPE_... list of constants.

Source Code
// Types of bonuses
public def TYPE_SLOW = 0;
public def TYPE_FAST = 1;
public def TYPE_CATCH = 2;
public def TYPE_GROW_BAT = 3;
public def TYPE_REDUCE_BAT = 4;
public def TYPE_GROW_BALL = 5;
public def TYPE_REDUCE_BALL = 6;
public def TYPE_STRIKE = 7;
public def TYPE_LIFE = 8;

// Total count of all bonuses
public def COUNT = 9;

...

public class Bonus extends CustomNode {
    // Type of the bonus
    public-init var type: Integer;

    // Width of the bonus (without shadow)
    public-read var width: Integer;

    // Height of the bonus (without shadow)
    public-read var height: Integer;

    ...

Figure 5: Bonus Class

When the game starts the splash screen is shown. It is represented by the Splash class (see Figure 6). The Splash class extends the CustomNode class and contains some animated effects. After a user presses any key or mouse button, the game begins. This behavior is implemented in the background field: The onMousePressed event catches mouse button actions and the onKeyPressed event catches keyboard actions.

Source Code
public class Splash extends CustomNode {
    ...
    
    def background = ImageView {
        focusable: true
        image: Config.images[Config.IMAGE_BACKGROUND]

        onMousePressed: function( e: MouseEvent ):Void {
            Main.mainFrame.startGame();
        }

        onKeyPressed: function( e: KeyEvent ):Void {
            Main.mainFrame.startGame();
        }
    }

Figure 6: Splash Class

The largest class in the sample is the Level class, as shown in Figure 7. It provides the full game process, for example, level visual representation, the bat moving, and the ball moving. Several states are available while a player plays the game. They are stored in the state field. When the state field contains 0, the message "READY" is shown to the user. As this state ends, the next state begins: state = 1. It means that the user can move the bat and the ball is caught (attached to the bat). When a player presses the mouse button or space button to begin moving the ball and get the game started, these actions are relative to state = 2. After a player has lost all lives, the last state begins: state = 4. The message "Game over" is shown on the screen. Pressing a mouse button or any key returns a player to the splash screen.

Source Code
public class Level extends CustomNode {
    ...

    // States
    // 0 - starting level
    // 1 - ball is catched
    // 2 - playing
    // 3 - game over
    var state = 0;

    ...

Figure 7: Level Class

Customizing the Code

Two utility files also are included in the sample. The first utility is the Utils.fx file, which provides some helpful mathematical functions. The second utility is the LevelData.fx file. It accumulates all the information for the levels in string format. If you want to add some new levels or modify existing levels, you should change this file.

How to Play

After the game is launched, you see a splash screen with some animated effects. To start the game, press any key or mouse button. The game is in play mode. You see a big game field with bricks. At the bottom of the field is a bat that you can move by pressing keyboard keys left and right. You can also move the bat by moving the mouse. As you can see, the game also features a ball that is caught by the bat. To launch the ball, press the space button or a mouse button. While the ball is in play, try to return the ball by using the bat. If you do not manage to connect with the bat and you lose the ball, one life is taken. After you lose your last life, the game is over.

The aim of each level in the game is to break all the bricks. Try to strike every available brick with the ball. Some bricks contain bonuses that will fall after the bricks are broken. When you capture bonuses, you gain some advantages. Some levels have bricks that cannot be broken at all. At those levels, you should break only breakable bricks.