Giving Duke Something to Play With
JavaFX technology enables you to write an interactive game with numerous sprites on the game field at the same time.
Understanding the Code
Several classes are shown in the source code. Every class represents an entity in the game, for example, Duke, Enemy, and Bonus.
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();
mainFrame = MainFrame {
title: "Dukeman"
resizable: false
scene: Scene {
fill: Color.BLACK
width: Config.SCREEN_WIDTH
height: Config.SCREEN_HEIGHT
}
}
// Run splash
mainFrame.state = 0;
}
Figure 1: Main.run Method
The run method accomplishes two objectives. The first objective is the initialization of the Config class. 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 content of the MainFrame.scene can contain either splash screen or a game level. The MainFrame class also contains public game properties such as the following:
- lifeCount - Number of remaining lives
- score - Current scores
- highScore - High 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 Duke class represents the main character in the game: Duke. See the details of the realization in Figure 2. Duke can move in four ortigonal directions. The current direction is stored in the direction property.
public class Duke extends CustomNode {
def image = ImageView { };
def direction = Direction { };
var lastRightDirection = true;
var animationIndex: Integer;
override public function create(): Node {
image
}
...
Figure 2: Duke Class
The Enemy class represents the enemy, as shown in Figure 3. Four enemies are in every level and each enemy has a different behavior.
public def STATE_HUNTING = 0; public def STATE_SAFE = 1; public def STATE_DEFEAT = 2; public def TYPE_0 = 0; public def TYPE_1 = 1; public def TYPE_2 = 2; public def TYPE_3 = 3; def MAX_MOVE_COUNT = 100; // Aggression level of enemies def ENEMY_AGGRESSION = [9, 7, 5, 3]; public class Enemy extends CustomNode { public-read var state: Integer; public-init var type: Integer; var animationIndex: Integer; var speed: Integer; var safetyTime: Duration; // > 0: The enemy is parked in enemy home // = 0: The enemy is unparking // < 0: The enemy is outside of enemy home var parkingTime: Duration; // This count increases after every move() invocation // It is used for accurate speed calculation var moveCount; def direction = Direction { }; def image = ImageView { }; var homeEntrance: Coord; public function initialize(level: LevelData): Void { ...
Figure 3: Enemy Class
Two kinds of dots are used in the Dot class. The first dot is a small coffee bean. The second dot is a cup of coffee. The small property describes the type of a dot, as shown in Figure 4.
public class Dot extends CustomNode {
public-init var small: Boolean;
def image = ImageView { }
var animationIndex: Integer;
override public function create(): Node {
if (small) {
image.image = Config.images[Config.IMAGE_SMALLDOT];
}
image
}
public function animate() {
animationIndex++;
if (animationIndex >= 20) {
animationIndex = 0;
}
image.image = Config.images[if (animationIndex < 10)
Config.IMAGE_DOT0 else Config.IMAGE_DOT1];
}
}
Figure 4: Dot Class
When the game starts the splash screen is shown. It is represented by the Splash class (see Figure 5). After a user presses any key, the game begins. This behavior is implemented in the background field: The onKeyPressed event captures keyboard actions.
public class Splash {
...
def background = ImageView {
focusable: true
image: Config.images[Config.IMAGE_SPLASH_BACKGROUND]
onKeyPressed: function( e: KeyEvent ):Void {
Main.mainFrame.startGame();
}
}
Figure 5: Splash Class
The main class in the sample is the Level class, as shown in Figure 6. It provides the complete game process. Each level contains level representation, duke, four enemies, and other entities. All animation, such as enemies and Duke movement, is represented in the timeline field.
def BONUS_SCORE = [200, 400, 800, 1600]; def STATE_GET_READY = 1; def STATE_PLAY = 2; def STATE_GAME_OVER = 3; public class Level { public-init var level: LevelData; def duke = Duke { } // Direction correspondent to keyboard def keyboardDirection = Direction { }; def enemies = [ Enemy { type: Enemy.TYPE_0 }, Enemy { type: Enemy.TYPE_1 }, Enemy { type: Enemy.TYPE_2 }, Enemy { type: Enemy.TYPE_3 }, ]; ...
Figure 6: Level Class
How to Play
You see a large maze with several coffee beans in it. You have to manipulate Duke with the Up, Down, Left, and Right keys, and collect all the beans.
Several enemies in the maze are represented by tanks. If any enemy captures you, you lose one life. Every level contains four coffee cups. If Duke collects one of them, he acquires an ability to capture enemies and scores more points for every captured enemy. To complete the level and move to another level, you need to collect all the coffee beans and coffee cups. Each next level is more difficult than the previous level: The enemies move faster and can capture you easier.

