Creating a Simple Game in JavaFX (Part 4)


In the previous entries we decided how our game should behave, we created the playing field and the player object. We now want to add the “enemy” objects to our game.

Creating the enemy objects

So far most of our game has been programmed in one file. For our enemies we will create a separate file and class named Enemy.fx


package myballgame;

import ...

public static def DEFAULT_FILL = Color.CRIMSON;

public class Enemy extends CustomNode {

    var radius = 10;
    var moveUp = false;
    var moveLeft = false;
    var speedX = 0;
    var speedY = 0;

    var enemy:Circle;

    override function create():Node {

        // Enemy Speed
        speedX = calcSpeed();
        speedY = calcSpeed();

        // Enemy initial Direction
        moveUp = calcRandomBoolean();
        moveLeft = calcRandomBoolean();

        // Enemy figure
        enemy = Circle {
            radius: radius
            fill: Color.CRIMSON
        }
        return enemy
    }

    function calcSpeed():Integer {
       var r = javafx.util.Math.random();
       if (r>0.66){
            return 3
        } else if (r<0.33) {
            return 2
        } else {
            return 1
        }
    }

    function calcRandomBoolean():Boolean {
        if (javafx.util.Math.random()>0.5)
            return true
        else
            return false
    }

    public function calcPosition(xMin:Integer,xMax:Integer,yMin:Integer,yMax:Integer) {
        if (moveUp) {
            var newY= translateY - speedY;
            if (newY > yMin + radius)
                translateY = newY
            else
                moveUp = false
        } else {
            var newY= translateY + speedY;
            if (newY < yMax - radius)
                translateY = newY
            else
                moveUp = true
        }
        if (moveLeft) {
            var newX = translateX - speedX;
            if (newX > xMin + radius)
                translateX = newX
            else
                moveLeft = false
        } else {
            var newX = translateX + speedX;
            if (newX

This is a big chunk of code but we will walk through it slowly.
The package definition at the top shows that this file is in the same package as our Main.fx file.
We then define the default fill color of our enemies.
Our Enemy class extends CustomNode which allows us to easily create our own Node objects to add to our Scene.
The variables radius, moveUp, moveLeft speedX, speedY and enemy will be explained later on.
The function create() defined in CustomNode must be overriden. This is were we initialize our object. We setup a random speed for our enemy in the x-direction and a different random speed for the y-direction of our enemy. We then give the enemy an initial random direction by setting moveUp and moveLeft to a random value calculated in calcRandomBoolean(). Finally we return a Node object that defines the shape of our Enemy. In our case a simple circle with a radius defined by the initialization of the radius variable.
At the end of the class we define a function that lets us calculate the Enemies new position. calcPosition(...) takes the bounds of our playing field as parameters and makes sure the new Enemy object position is within these bounds. If it is not it switches the Enemies direction. For example if the Enemy object is about to leave the playing field at the top. calcPosition(...) changes moveUp to false and in the next cycle the Enemy object will be moving downward, until it hits the lower edge of the playing field and the y-direction is again reversed.

Displaying the enemy objects

Now that we have defined an Enemy class it is time to display our enemies. This is done with the following changes to Main.fx


var player ...

var enemies : Enemy[] = for (i in [0..3]) {
    var enemy:Enemy = Enemy {
        translateX : randomEnemyInitPosition(), translateY:randomEnemyInitPosition()
    }
    enemy
};

var timeline ...

function randomEnemyInitPosition():Integer {
    var r = javafx.util.Math.random();
    var r2 = javafx.util.Math.random();
    ((r  * 100)+(r2*100)) as Integer
}

Stage {
    ...
    scene: Scene {
        ...
        content: Group {
            ...
            content: [
                playingField,
                player,
                enemies
            ]
            ...
        }
    }
}

We define an variable of type Enemy Array and initialize it with four Enemy objects. Each enemy object gets a random position within a 100 x 100 pixel area of the playing field assigned.
We then add our Enemy Array to the contents of our Group object just like we added our playing field and player.
The result looks like the following:

img4

Animating the Enemies
Now we would like to animate our enemy objects. As with the player the new enemy position is calculated with every cycle in our game play and calling the recalculate enemy position method must therefore be located in our timeline code:


var timeline: Timeline = Timeline {
    ...
    keyFrames : [
        KeyFrame {
            ...
            action: function() {
                // Enemy
                for (enemy in enemies)
                 enemy.calcPosition(playingField.boundsInLocal.minX,
                             playingField.boundsInLocal.maxX,
                             playingField.boundsInLocal.minY,
                             playingField.boundsInLocal.maxY);
            ...

So with each cycle we call the calcPosition(...) function of each player object and each enemy knows how to calculate its new position as we have seen earlier when we discussed the calcPosition(...) function.

In the next part we will deal with calculating collisions.

Tags: , ,

Leave a Reply