Build a Brick Breaking Video Game
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.
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.
// 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.
// 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.
// 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.
// 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.
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.
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.

