Tower Defense in JavaFX (6)

So we’re at part 6 of this tutorial already, and the game has come a long way in the meantime. In this part we’ll finally add a Layer that shows the score, the number of Enemies that have reached their target, a button to start the next Wave, and the money left to buy new turrets. Speaking of money, we haven’t got the logic for that yet, so we should fix that first. I didn’t want to store the price in the application code though, because it makes it harder for my imaginary level designer (who doesn’t know programming) to finetune the variables. Also I wanted to keep everything in one place, so if we decide to add a new turret, it shouldn’t be required to make changes in many different places. Fortunately we can store properties in a TileSet, so we’ll do it like that.

The TMX format for Tilemaps is really a great way to separate the design from the programming internals (business logic sounds really wrong, when it comes to games). As a result graphic designers can create the assets, level designers can create the levels, and even users can mod the game and create their own levels very easily. So far we used the Tiled editor to create a level, and also stored some meta information about the spawning of the enemies and the attack path in one of the layers. Now we add some properties to individual Tiles of the TileSets. This only works with internal TileSets, so in Tiled you have to define a new Tileset via “Map -> new Tileset”. If you now rightclick a Tile it has an action to define the Tile Properties. I’ve defined some for my tower-bases:

I’ve added a couple of properties including a range for the weapon,  the rate it’s firing, the damge it causes, name and description to display later on, and a type, I want to use to define the tiles to use as a bullet. Maybe there will also beweapons like a laser that have to be implemented in a different way. Then I can decide to use different logic tepending on that type. I get the properties from the Tile via the Tileset like this:

Properties properties = tileSet.getTileList().get(selectedIndex).getProperties();

Inside the CannonSprite I can use them like this:

String rangeProperty = properties.getProperty("range");
if (rangeProperty != null) {
range = Integer.parseInt(rangeProperty);
}
String damageProperty = properties.getProperty("damage");
if (damageProperty != null) {
damage = Integer.parseInt(damageProperty);
}
String rateProperty = properties.getProperty("firerate");
if (rateProperty != null) {
rate = Float.parseFloat(rateProperty);
}//....

We’ll do the same thing with the EnemySprites, so we can determine the points you get for destroying them, their resistance to damage, maybe a recovery rate, etc.. One of the nice things of this approach is, that it’s also very easy to extend. In case I decide later to add a new turret that creates a magnetic field to confuse the enemy and make them fly slower, I can do that by simply adding a new Property to that particular turret. I do not have to update my old level designs or break the custom levels of my users. It’s similar to one of my favorite programming techniques, “composition over inheritance” in that aspect.

We can now use these properties and e.g. have the turrets fire at different rates by modifying the evaluation interval of their FireBehavior:

 @Override
public long getEvaluationInterval() {
return (long)(2000000000*rate);
}

And we get this as a result:

The second  turret now fires at a higher rate, while causing less damage with each of it’s bullets. If we combine that with different prices for the turrets and the limited space available to place turrets, we already have some of the strategic elements that make Tower Defense an interesting game. Now our level designer is in charge to design the levels, set the prices and other properties, to make the game interesting to play, while we continue adding more features.

Now let’s start with the HUD Layer and simply display the score:

    private class HUD extends Layer {

@Override
public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
graphicsContext.setFill(Color.RED);
graphicsContext.setFont(Font.font("OricNeo", 12));
graphicsContext.fillText("Score: "+score, 10, 16);
}

}

The score is defined as an IntegerProperty in my game (this is NOT a JavaFX property!) and passed to the EnemySprites:

private IntegerProperty score = new IntegerProperty(0);

So in the EnemySprites “die” method, we simply increase that value:

@Override
public void die() {
super.die();
getParent().addSprite(new Sprite(getParent(), explosionAnimation, "explosion", getX() - 30, getY() - 80, 128, 128, Lookup.EMPTY));
score.set(score.integerValue()+killPoints);
}

Now add the HUD as the Top Layer, and you can see the score:

What we need next is some way to start the wave. In order to do that, it would be great to have some kind of simple control, like a button. We could add that in different ways. The simplest way would probably be to put the canvas in a StackPane add an AnchorPane on top and add a JavaFX Node or Control to that. But we want to do it with FXGameEngine features only, so we’ll use a Sprite as a Button:

final Sprite button = new Sprite(canvas, "button",  tileMap.getWidthInPixels()- 30, 20, 20, 20, Lookup.EMPTY);
button.setOnMouseClicked(new MouseEventHandler() {

@Override
public void handle(MouseClick click) {
startWave();
button.die();
}
});
canvas.addSprite(button);

I’m pretty sure that the API for EventHandling will still change a bit, but it’ll stay an abstraction similar to this. This Sprite has no renderer, so a default renderer will be used that simply paints a Rectangle:

That’s it for today. In the next part we’ll add money to the game, so it get’s a bit more interesting…

 

 

Tower Defense in JavaFX (5)

This is part 5 of this ongoing tutorial on creating a Tower Defense Game in JavaFX using the FXGameEngine. The enemies now fly on their attack path to the target, and the Turrets aim and shoot at them. So the most important parts are there, but there are still a lot of details missing. The game simply starts and doesn’t give us a chance to prepare for the next wave. It doesn’t show the score (actually there is no score yet). The wave does never end, and you can’t win or loose. So we need to add some game logic a HUD and Controls. We also need to see the damage status of the Enemies. The last one is the feature, we’ll care about in this part of the tutorial.

StackedRenderer

So let’s start with the Enemy status. We could add those in an extra HUD Layer, since that’s what a HUD is for, but I prepare to do it via Renderers. Every Sprite has one current Renderer. You can switch Renderers, and that’s what SpriteActions do (we’ll treat those in a different tutorial), but you still can only have one active Renderer at once. In our EnemySprite it’s the LookAheadTileSetAnimation. In order to allow a combination of Renderers, you can use the StackedRenderer class. It allows you to stack an unlimited number of Renderers, and simply delegates to them on every method call.

So we can create a simple HealthBarRenderer and use that:

public class HealthBarRenderer implements Renderer {

@Override
public boolean prepare(Sprite sprite, long time) {
return true;
}

@Override
public void render(Sprite sprite, GraphicsContext context, float alpha, long time) {
EnemySprite enemySprite = (EnemySprite) sprite;
double health = enemySprite.getHealth();
double maxHealth = enemySprite.getMaxHealth();
if (health == maxHealth) {
return;
}

int width = sprite.getWidth();
int height = sprite.getHeight();
double percent = health / maxHealth;
context.setFill( Color.rgb(200,200,200,.5));
context.fillRect(4+(width / 2), 10+(height / 2), (width / 2), 4);

context.setFill( Color.rgb(0,255,0,.5));
if (percent < .5) {
context.setFill(Color.rgb(255,255,0,.5));
}
if (percent < .2) {
context.setFill(Color.rgb(255,0,0,0.5));
}
context.fillRect(4+(width / 2), 10+(height / 2), (width / 2 * percent), 4);
}
}

And we use that in a StackedRenderer:

        final TileSetAnimation tileSetAnimation = new LookAheadTileSetAnimation(enemy1, new int[]{0, 1, 2, 3, 4, 5}, 10f);
final StackedRenderer stacked = new StackedRenderer(tileSetAnimation, new HealthBarRenderer());

And this is what we get:

In the next part we’ll add the HUD.

Tower Defense in JavaFX (4)

Ok, so far we’ve created a TileMap, displayed it on screen and made it editable in the first part. In the second part we implemented calculation of the attack path using the A* algorithm and made the enemies follow that path. In part three we created some custom TileSetAnimations so we can rotate the Insectoids around their center by an angle. We then applied this to the Insectoids, so they look forward when they fly, and to the turrets, so they always aim at the closest target. Time to make the turrets shoot at their enemies.

First we need TileSets for an explosion and a bullet for the Pellet gun. I found a nice free explosion sheet here. It’s a bit larger (128*128) than the sprites, and the explosion doesn’t start in the center, but after a bit of fiddling with the position relative to the exploding insectoid it works quite nicely. I created the bullet myself and I know I have to come up with something better :-), but at least it’s something that is visible on the screen. After some fiddling with the correct initial position I created this BulletLaunching Behavior:

new SpriteBehavior() {
@Override
public boolean perform(Sprite sprite) {
double angle = rotateAnimation.getAngle();
double xVelocity = Math.cos(Math.toRadians(angle));
double yVelocity = Math.sin(Math.toRadians(angle));
final double centerX = x + (width / 2);
final double centerY = y + (height / 2);
double startX = centerX + (xVelocity * (width / 2)) - 4;
double startY = centerY + (yVelocity * (height / 2)) - 4;
Sprite bullet = new Sprite(getParent(), shoot, "bullet" + (bulletCounter++), startX, startY,
8, 8, Lookup.EMPTY);
bullet.setVelocityX(xVelocity);
bullet.setVelocityY(yVelocity);
// add bullet behavior

return true;
}

@Override
public long getEvaluationInterval() {
return 2000000000; //To change body of generated methods, choose Tools | Templates.
}
});

Most of the code is calculating the initial position and making sure the bullet heads off in the right direction. Now we need to add some collision detection. Some systems do have a centralized collision system, and allow to add listeners. I prefer to again do this via behaviors, because I find it more natural and intuitive that the bullet itself checks if it has hit something:

 bullet.addBehaviour(new SpriteBehavior() {
private double range = 75;

@Override
public boolean perform(Sprite sprite) {
Collection checkCollisions = sprite.getParent().checkCollisions(sprite);
for (Collision collision : checkCollisions) {
if (collision.getSpriteOne() instanceof EnemySprite) {
sprite.getParent().removeSprite(sprite);
((EnemySprite) collision.getSpriteOne()).hit(6);
return false;
} else if (collision.getSpriteTwo() instanceof EnemySprite) {
sprite.getParent().removeSprite(sprite);
((EnemySprite) collision.getSpriteTwo()).hit(6);
return false;
}
}
if (distance(sprite.getX(), sprite.getY(), centerX, centerY) > range) {
sprite.getParent().removeSprite(sprite);
return false;
}
return true;
}
});

What we do here is simply ask the GameCanvas for collisions of this specific Sprite and try to cause damage if it’s an enemy. The anonymous inner bullet Sprite and the Behavior will be converted later to regular classes to make the code nicer and to make them easier to create and configure. On the side of the Enemy Sprite we need to implement the “hit” method:

public void hit(int impact) {
power = power - impact;
if (power getParent().removeSprite(this);
getParent().addSprite(new Sprite(getParent(), explosionAnimation, "explosion", getX() - 30, getY() - 80, 128, 128, Lookup.EMPTY));
}
}

Very simple: in case the hit was deadly, we remove the Sprite and add an Explosion Sprite. If the sprite sizes would match, we could have simply set the explosionAnimation on the existing Sprite. If you can create your own SpriteSheets, you should do that, it makes live much easier. The ExplosionAnimation is configured to run only once, and it has an EventHandler that removes the Sprite, once the Animation is complete:

explosionAnimation = new TileSetAnimation(explosion, 100f);
explosionAnimation.setRepeat(1);
explosionAnimation.setOnFinished(new AnimationEventHandler() {
@Override
public void handleEvent(AnimationEvent event) {
Sprite target = event.getTarget();
target.getParent().removeSprite(target);
getParent().removeSprite(EnemySprite.this);
}
});

That’s it. Our Turrets will now fire Bullets at the Enemies and try to hurt them until they explode:

In the video you also see a DebugLayer. Currently it tracks some performance data, mainly FPS and if the time between two pulses is too long. I’ve also added a bullet to the top of the screen, to visually detect stuttering animations. You can safely ignore that…

So we’ve got almost everything we need for a Tower Defense Type game now. In the next part of this Tutorial, we’ll add damage indicators to the enemies, and a HUD with the score and a control to start the next wave.

Tower Defense in JavaFX (3)

In the last part you saw how you can create Sprites, animate them, and give them Behavior. But the animation isn’t very nice, because as an Insectoid you’re supposed to always look where you fly. Remember: Safety first! We can do that very easily by creating a custom TileSetAnimation:

public class RotatingTileSetAnimation extends TileSetAnimation {

private double angle = 0;

public RotatingTileSetAnimation(TileSet set, int[] indices, float speed) {
super(set, indices, speed);
}

public void setAngle(double angle) {
this.angle = angle;
}

@Override
public void render(Sprite sprite, GraphicsContext context, float alpha, long delta) {
context.save();
context.translate(sprite.getWidth() / 2, sprite.getHeight() / 2);
context.rotate(angle);
context.translate(-sprite.getWidth() / 2, -sprite.getHeight() / 2);
super.render(sprite, context, alpha, delta); //To change body of generated methods, choose Tools | Templates.
context.restore();
}
}

We can calculate the rotation angle from the x and y velocity and set it on our GraphicsContext before rendering. So here’s a subclass that does that:

public class LookAheadTileSetAnimation extends RotatingTileSetAnimation {

public LookAheadTileSetAnimation(TileSet set, int[] indices, float speed) {
super(set, indices, speed);
}

@Override
public void render(Sprite sprite, GraphicsContext context, float alpha, long delta) {
setAngle(Math.toDegrees(Math.atan2(sprite.getVelocityY(), sprite.getVelocityX())));
super.render(sprite, context, alpha, delta); //To change body of generated methods, choose Tools | Templates.
}
}

Here’s the result:

Fairly simple, isn’t it? Now the next step will be to add some behavior to the turets themselves. I want them to always check for the closest enemy and point the cannon at it. First I changed the code a bit and separated the turrets into bases and cannons again. So when you select a cannon now, a turret-base will be placed in the TileLayer named “turret-bases”. I simply changed the TurretView class to support this:

class TileSetView extends StackPane {

Canvas canvas;
TileSet cannons;
TileSet bases;
int selectedIndex = 0;
Color selected = Color.rgb(100, 100, 255, .2);

public TileSetView() {
}

public void setTileSet(final TileSet bases, final TileSet cannons) {
this.cannons = cannons;
this.bases = bases;
getChildren().clear();
ImageView turretBases = new ImageView();
turretBases.setImage(bases.getTileImage());

ImageView turretCannons = new ImageView();
turretCannons.setImage(cannons.getTileImage());

getChildren().addAll(turretBases, turretCannons);

canvas = new Canvas(cannons.getTileImage().getWidth(), cannons.getTileImage().getHeight());
getChildren().add(canvas);
canvas.setOnMouseClicked(new EventHandler() {
@Override
public void handle(MouseEvent t) {
double x = t.getX();
double y = t.getY();
selectedIndex = (int) ((int) x / cannons.getTilewidth() + (((int) y / cannons.getTileheight()) * cannons.getNumColumns()));
updateCanvas();
}
});
updateCanvas();
}

public int getSelectedGid() {
if (bases == null) {
return -1;
}
return bases.getFirstgid() + selectedIndex;
}

public int getSelectedIndex(){
return selectedIndex;
}

public void updateCanvas() {
GraphicsContext graphicsContext2D = canvas.getGraphicsContext2D();
graphicsContext2D.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
if (selectedIndex >= 0) {
graphicsContext2D.setFill(selected);
int x = selectedIndex % cannons.getNumColumns();
int y = selectedIndex / cannons.getNumColumns();
graphicsContext2D.fillRect(x * cannons.getTilewidth(), y * cannons.getTileheight(), cannons.getTilewidth(), cannons.getTileheight());
}
}
}

So this is what it looks like now:

Next we add the cannons. While the turret-bases are simpel Tiles, our Cannons need to be Sprites, so we can add Behavior to them:

public class CannonSprite extends Sprite {

RotatingTileSetAnimation rotateAnimation;

public CannonSprite(GameCanvas parent, RotatingTileSetAnimation animation, String name, double x, double y, int width, int height) {
super(parent, animation, name, x, y, width, height, Lookup.EMPTY);
this.rotateAnimation = animation;
addBehaviour(new SpriteBehavior() {
@Override
public boolean perform(Sprite sprite) {
Sprite closest = null;
double dist = Double.MAX_VALUE;
Collection sprites = sprite.getParent().getSprites();
for (Sprite sprite1 : sprites) {
if (sprite1 instanceof EnemySprite) {
double distance = distance(getX(), getY(), sprite1.getX(), sprite1.getY());
if (distance < dist) {
dist = distance;
closest = sprite1;
}
}
}
if (closest != null) {
rotateAnimation.setAngle(Math.toDegrees(Math.atan2(closest.getY() - sprite.getY(),closest.getX() - sprite.getX())));
}

return true;
}
});
}

public double distance(double x1, double y1, double x2, double y2) {
return Math.sqrt(
(x1 - x2) * (x1 - x2)
+ (y1 - y2) * (y1 - y2));
}
}

Again I’m using a RotatingTileSetAnimation and simply set the angle so the cannon points at the closest Enemy. Here’s what we get:

That’s it for this part of the tutorial. We’ve created some custom animations to make the Insectoids look in the right direction and also to make the Turrets always point at the closes target. As you can see from these examples, the Game Engine tries to make it really simple to add Behavior to the Sprites. In the next part we’ll make the turrets fire at their Enemies.

Tower Defense in JavaFX (2)

In the last part we’ve created a simple editor that let’s us place turrets. Now we’ll add a spawnpoint where the enemies originate, and define an attack target for them. First I’ll add some more information to the map via an Object Layer. That’s standar TMX, so we can do it in the TileMap Editor:

In order to calculate the attack path for our enemies, we’ll use the A* Algorithm, which is part of the tilengine module:

So let’s get the spawnpoint and target and store them for our algorithm:

ArrayList objectGroups = tileMap.getObjectGroups();
for (ObjectGroup objectGroup : objectGroups) {
for (final TObject tObject : objectGroup.getObjectLIst()) {
if (tObject.getName().equals("spawnpoint")) {

spawnpointX = tObject.getX() / turrets.getTilewidth();
spawnpointY = tObject.getY() / turrets.getTileheight();

}

if (tObject.getName().equals("target")) {

targetX = tObject.getX() / turrets.getTilewidth();
targetY = tObject.getY() / turrets.getTileheight();

}
}
}

With these values we can initialize the A* algorithm that calculates the shortest path for our enemies:

AStar.AStarTile start = new AStar.AStarTile((int) spawnpointX, (int) spawnpointY);
AStar.AStarTile end = new AStar.AStarTile((int) targetX, (int) targetY);
attackPath = AStar.getPath(tileMap, platformLayer, start, end);

In order to see the result , we’ll add a debug layer to the GameCanvas:

private class AStarLayer extends Layer {
public AStarLayer() {
}
Color pathColor = Color.rgb(255, 100, 100, .2);

@Override
public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
AStar.PathNode start = attackPath;
if (start != null) {
graphicsContext.setFill(pathColor);
graphicsContext.fillRect(start.getX() * tileMap.getTilewidth(), start.getY() * tileMap.getTileheight(), tileMap.getTilewidth(), tileMap.getTileheight());
while (start.getParent() != null) {
start = start.getParent();
graphicsContext.fillRect(start.getX() * tileMap.getTilewidth(), start.getY() * tileMap.getTileheight(), tileMap.getTilewidth(), tileMap.getTileheight());
}
}
}
}

The result looks like this:

You see the shortest path in red color. Since the algorithm doesn’t “see” the structures of the background image, it calculates the path accordingly, and the enemies would simply ignore the structures of the ship (the background is supposed to be a part of a spaceship). To fix this, we’ll add some invisible Tiles later. For larger games it’s better to use an invisible collision layer though, which gives you better performance and more ways to implement things like locked passages. For us the transparent-tile-approach is better, because we don’t need an extra layer, and it’s easier if the user can edit the layout.

Now we need to send the enemies down this path. In order to animate the Sprite I combined the animation phases into a single image:

Now we can use the Tiled editor to create a TileSet from it:

I also used Tiled to add two additional properties to the spawnpoint:

The first one defines how many enemies I want to spawn of each type, the second one defines the pause between their spawning. I doubt that they’ll stand the test of time unchanged, but for now let’s work with them. Inside the code for reading the ObjectGroups we can access the Properties:

if (tObject.getName().equals("spawnpoint")) {

Properties properties = tObject.getProperties();
evaluationInterval = Long.parseLong(properties.getProperty("delay"));
spawnpointX = tObject.getX() / turrets.getTilewidth();
spawnpointY = tObject.getY() / turrets.getTileheight();

}

For now we only have one type of monster, so we can ignore that and only use the delay. First we’ll create a SpriteAnimation from our TileSet:

final TileSet enemy1 = tileMap.getTileSet("enemy1");
final TileSetAnimation tileSetAnimation = new TileSetAnimation(enemy1, new int[]{0, 1, 2, 3, 4, 5}, 10f);

In order to Spawn a monster we’ll define a Behavior. That’s simply a timed method call. The API will probably be changed a bit here in order to support Lambda expressions:

Behavior monsterSpawnBehavior = new Behavior() {
int enemyCount = 0;

@Override
public boolean perform(GameCanvas canvas, long nanos) {
new Sprite(canvas, tileSetAnimation, "enemy" + (enemyCount++), ((int)spawnpointTileX) * tileMap.getTilewidth(), ((int)spawnpointTileY) * tileMap.getTileheight(), 46, 46, Lookup.EMPTY);
return false;
}
};
monsterSpawnBehavior.setEvaluationInterval(evaluationInterval);
canvas.addBehaviour(monsterSpawnBehavior);

So now every soandso nanoseconds a new Enemy will be added to the playfield. We’ll probably create an EnemySprite class later to encapsulate the Behavior. But for now let’s stick with this Sprite and add Behaviour to it:

sprite.addBehaviour(new SpriteBehavior() {
AStar.PathNode start = attackPath;

@Override
public boolean perform(Sprite sprite) {
double x = sprite.getX();
double y = sprite.getY();
double pathX = start.getX() * tileMap.getTilewidth();
double pathY = start.getY() * tileMap.getTileheight();
if (Math.abs(pathX- x) 1) {
sprite.setVelocityX(.5);
} else if (pathX- x < -1) { sprite.setVelocityX(-.5); } else { sprite.setVelocityX(0); } if (pathY - y > 1) {
sprite.setVelocityY(.5);
} else if (pathY - y < -1) {
sprite.setVelocityY(-.5);
} else {
sprite.setVelocityY(0);
}
return true;
}
});

And here’s the result:

That’s it for now. As you can see, it’s pretty simple to add AI to the sprites via Behaviors and AStar comes pretty handy. In the next part we’ll take care that our enemies point in the right direction, and add some Behavior to the turrets.

Tower Defense in JavaFX

I wanted to use my Game Engine to write a Tower Defense game for a long time, but since there was an effort to create a JavaFX Tower Defense Game by another group, I thought I should rather create a different game instead. From the mailing list I learnt, that the other game is no longer being developed. So I decided I should give it a try.

Tower Defense is a game that is perfect for a Tile based approach, so I started to look for some tiles. I found some here and the artist, Silviu Ploisteanu, gave me the permission to use them in my demo. Tower Defense as a game is very similar to a TileMap editor, so I’ll be able to reuse a lot of code from the Editor I’ve created a while ago:

The first thing to do was to combine the individual graphics into images for tiling. I grouped the enemies, the turret bases, the cannons, the terrain, and also created a single tile tileset for the background. After that I used a TileMap Editor (http://www.mapeditor.org/) to create TileSets from these images. I’ll probably have to do that part again, because Gimp changed the colors in this process, but for now I don’t care too much about the visuals.

Then I added 5 layers from bottom to top to a new map: “background”, “terrain”, “turret-bases” and “turret-cannons”, and “enemies”. There will be a “bullets and explosions” layer as well, but I’ll add that manually later on. So far, no coding is involved, and through the TileMap based approach we already have a serialization format for the game. So when the user edits the map, we can simply store the changes to a TileMap. This is how the map looks now:

The next step is the coding. I simply created a new JavaFX Application with a BorderPane. In the center I’ll have the playfield, to the right I’ll have a palette with cannons.

This is how you create the GameCanvas:

tileMap = TileMapReader.readMap(fileURL);
canvas = new GameCanvas(tileMap.getTilewidth() * tileMap.getWidth(), tileMap.getHeight() * tileMap.getTileheight(), tileMap.getTilewidth() * tileMap.getWidth(), tileMap.getHeight() * tileMap.getTileheight());
// add all the layers
ArrayList layers = tileMap.getLayers();
for (TileMapLayer tileMapLayer : layers) {
  canvas.addLayer(tileMapLayer);
}

Then I get the TileSet with the cannons and create the Palette. I’m using a VBox for the Palette, since I want to make the terrain editable as well, and I might add another TileSet for that later:

TileSet turrets = tileMap.getTileSet("turrets");

TileSetView turretView = new TileSetView(turrets);
VBox palette = new VBox();
palette.getChildren().addAll(turretView);

The TileSetView simply displays the Base image, and allows you to select a Tile via mouse click. I’ll maybe show the code a bit later, when we start with the user interaction. For now this is what we see after launching the game:

The ugly red area is the default background. Unfortunately the background image Dimension isn’t a multiple of the tile size, so I’ll have to define an offest later on. For now I’ll ignore it, it’s just a backgound…

Now we want the user to be able to place turrets. For now the player has an unlimited amount of money, so he can place as many turrets as he wants. The only constraint is, that he can only place them if he’s on a platform, and if there’s not a turret already. So we add an EventHandler for Mouse Events that get’s the selected Turret from the Palette, and adds it to the turret-base layer. For the sake of simplicity, I’ve combined the turret-base and the turret into one image for now:

canvas.setOnMousePressed(new EventHandler() {
@Override
public void handle(MouseEvent t) {

double x = t.getX();
double y = t.getY();

int idx = (int) ((int) x / tileMap.getTilewidth() + (((int) y / tileMap.getTileheight()) * tileMap.getWidth()));
if (platformLayer.getGid(idx) != 0 && turretBaseLayer.getGid(idx)==0) {
turretBaseLayer.getData().setGid(idx, turretView.getSelectedGid());
}
}
});

As a result, we can now place turrets whereever there’s a platform and no turret yet:

I think that first part already shows, that the API is really nice to create simplegames without having to write an awful lot of code. In the next part we’ll add the spawnpoint and the target and use A* to calculate the enemies path.

Sliding Tasks: ReScheduler for JavaFX

In the TouchWheel control I wrote for touch devices, I wanted to have a little “snap to grid” animation after a user scrolled to a value. You can see it in this video:

You get informed of the end of a Scroll Event in JavaFX by an event of EventType “ScrollEvent.SCROLL_FINISHED”. That’s not the last of the SrollEvents you’ll recieve though. ScrollEvent.SCROLL_FINISHED just informs you that the user input is over, but you’ll get more events due to inertia. If you ignore the events due to inertia (ScrollEvent.isInertia()), everything is fine, and you can do what you want. But if you allow inertia in a JavaFX scroll event, you have no way of telling when scrolling is over. The continuous events will simply stop after a while.

I looked around for a helper class that would allow me to coalesce all the events that arrive in a certain period of time and execute the Task just once, but since I didn’t find anything except for the NetBeans RequestProcessor. Since I needed to have a way to do the Animation when the series of events has finished,I created this little ReScheduler:

public class ReScheduler {

    private final Runnable runable;
    private final long delay;
    private TimerTask task;
    private final Timer timer = new Timer();

    public ReScheduler(Runnable runnable, long delay) {
        this.runable = runnable;
        this.delay = delay;
        task = new TimerTask() {
            @Override
            public void run() {
                runable.run();
            }
        };
    }

    public void schedule() {
        task.cancel();
        task = new TimerTask() {
            @Override
            public void run() {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        runable.run();
                    }
                });
            }
        };
        timer.schedule(task, delay);
    }
}

With this little Utility class, you can schedule a Runnable as often as you like. If you schedule it again before a time period indicated by the delay, the old task will be canceled, and only if there is no new call before the specified delay, the task will be executed. It’s quite handy for tasks like this, although I’d prefer to have an event of EventType ScrollEvent.INERTIA_FINISHED.

Responsive Design with JavaFX

With CSS technologies it’s relatively easy to create a responsive design for your website. Depending on the sizeof the screen you can use a different CSS File and Layout. In JavaFX this might seem a bit harder at first sight, since CSS is only responsible for styling, but not for the layout. But using different FXML Files for the respective screen sizes and sharing a Controller is actually not that bad, and this way you can easily use SceneBuilder to preview the layouts on different screen sizes. In the code you either switch between the FXML-files depending on screen size, or you get rid of this by creating different deployments depending on the targeted device and copying the required files as part of your build.

But responsive design is not only be about size. For touch enabled devices it’s important to also use the right controls. At JayDay Jim Weaver gave an excellent presentation of how to create touch enabled UIs using JavaFX. You can download the presenation from the JayDay site: JayDay 2013. One of the things he proposes is to use the Pagination control, which is easier to use on a touch device than a TabPane.

Unfortunately that wouldn’t work well with our approach of reusing the controller, so we would have to create a Baseclass for the controller that has the common stuff and use

@FXML private Pagination pager

for the Touch device and

@FXML private TabPane pager

if the application runs on the desktop.

It would be much nicer if we could simply switch the Skin via CSS, as Paru Somashekar showed at JavaONE in her presentation with Jonathan Giles. In this presentation she shows an alternative Skin for a CheckBox, that can simply be set via CSS ( at around 27:15 ):

So you either create a deployment specifically for touch devices, that has the CSS, or you use something like this to find out if the device supports touch:

PlatformImpl.isSupported(ConditionalFeature.INPUT_TOUCH);

I decided to give it a try myself and convert the ChoiceWheel Control I created to a Skin for ChoiceBox:

For the FXML/CSS based approach I would not set the style via a styleclass on every CheckBox like Paru did, but enable the alternative Skin for all the Controls. In my case for all ChoiceBoxes:

.choice-box {
-fx-skin: "de.eppleton.controls.choicewheel.ChoiceBoxSkin";
}

No changes are required to the Controller. I think that’s a nice way to create an application that looks great on desktop as well as on a tablet or phone and it shows the strength of both FXML and CSS. Having a CSS-FXML pair per device size enables you to:

  • Organize the layout on a per device basis
  • Easily preview the design as it appears on different devices in SceneBuilder
  • Switch between different controls (touch / mouse) via CSS
  • Reuse the same Controller
  • Create deployments that only come with the required files for the targeted device

It would really be great to have more of these touch optimized Skins for common controls.

A star is born

I’ve just implemented the A* algorithm for the TileEngine. Might come handy for calculating the attack path in a “Tower Defense” style game, or for moving to  a location in touch based games like “Emily”.

import de.eppleton.fx2d.tileengine.TileMap;
import de.eppleton.fx2d.tileengine.TileMapLayer;
import java.util.*;

public class AStar {

    public static PathNode getPath(TileMap map, TileMapLayer layer, AStarTile start, AStarTile goal) {

        boolean[][] playField = new boolean[map.getWidth()][map.getHeight()];
        for (int x = 0; x < playField.length; x++) {
            boolean[] bs = playField[x];
            for (int y = 0; y < bs.length; y++) {
                int idx = x + (y * map.getWidth());
                bs[y] = layer.getGid(idx) == 0;
            }
        }

        PriorityQueue<PathNode> openList = new PriorityQueue<>();
        List<PathNode> closedList = new ArrayList<>();
        PathNode stn = new PathNode(null, 0, 0, start.x, start.y);
        openList.add(stn);
        while (!openList.isEmpty()) {
            PathNode toExpand = openList.poll();
            List<PathNode> successors = new ArrayList<>();
            int ex = toExpand.getX();
            int ey = toExpand.getY();
            addSuccessor(playField, ex - 1, ey, toExpand, goal, successors);
            addSuccessor(playField, ex, ey - 1, toExpand, goal, successors);
            addSuccessor(playField, ex + 1, ey, toExpand, goal, successors);
            addSuccessor(playField, ex, ey + 1, toExpand, goal, successors);
            for (PathNode candidate : successors) {
                if (candidate.x == goal.x && candidate.y == goal.y) {
                    return candidate;
                }
                if (alreadyFound(candidate, closedList)) {
                    continue;
                }
                if (alreadyFound(candidate, openList)) {
                    continue;
                }
                openList.add(candidate);

            }
            closedList.add(toExpand);
        }
        return null;
    }

    public static void addSuccessor(boolean[][] playField, int ex, int ey, PathNode toExpand, AStarTile goal, List<PathNode> successors) {
        if (ex < 0 || ey < 0 || ex >= playField.length || ey >= playField[ex].length) {
            return;
        }

        if (playField[ex][ey]) {
            PathNode n = new PathNode(toExpand, ex, ey);
            n.g = g(n);
            n.h = h(n, goal);
            n.f = n.g + n.h;
            successors.add(n);
        }
    }

    private static boolean alreadyFound(PathNode n, Collection<PathNode> l) {
        for (PathNode no : l) {
            if (no.getX() == n.getX() && no.getY() == n.getY() && no.getF() <= n.getF()) {
                return true;
            }
        }
        return false;
    }

    private static float g(PathNode n) {
        PathNode p = n.getParent();
        return p.g + 1;
    }

    private static float h(PathNode act, AStarTile goal) {
        int distX = Math.abs(act.getX() - goal.x);
        int distY = Math.abs(act.getY() - goal.y);
        float ret = (float) Math.sqrt(distX * distX + distY * distY);
        return ret;
    }

    public static class AStarTile {

        int x, y;

        public AStarTile(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static class PathNode implements Comparable<PathNode> {

        private PathNode parent;
        private float f, g, h;

        public PathNode getParent() {
            return parent;
        }
        private int x, y;

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        float getF() {
            return f;
        }

        private PathNode(PathNode parent, float g, float h, int x, int y) {
            this.parent = parent;
            this.x = x;
            this.y = y;
            this.g = g;
            this.h = h;
            this.f = g + h;
        }

        private PathNode(PathNode parent, int x, int y) {
            this(parent, 0, 0, x, y);
        }

        @Override
        public int compareTo(PathNode o) {
            return f > o.f ? 1 : f == o.f ? 0 : -1;
        }
    }
}

Here I use it in the tilemap editor:

And here in a game prototype:

Placing obstacles forces recalculation (the guys who get stuck when I place a rock can’t find a path ’cause the start tile is invalid):

 

 

Customizing JavaFX ListView

I’ve started to create a TileMap Editor for my JavaFX game engine. In this editor I’ve got a ListView that displays the Layers. Each Layer is represented by it’s name and a CheckBox is displayed next to it, so you can choose if you want to show or hide the layer:

The Layer List of my TileMapEditor

But simply adding the TileMapLayer objects into a ObservableList and displaying them with a ListView will result in this:

Displaying a TileMapLayer in a ListView with default rendering

In order to display the CheckBox, we need to do this:

layerList = FXCollections.observableArrayList(tileMap.getLayers());
        listView = new ListView<TileMapLayer>();
        listView.setItems(layerList);
        if (!layerList.isEmpty()) {
            listView.getSelectionModel().select(layerList.get(0));
        }
        Callback<TileMapLayer, ObservableValue<Boolean>> getProperty = new Callback<TileMapLayer, ObservableValue<Boolean>>() {
            @Override
            public BooleanProperty call(TileMapLayer layer) {

                return layer.getVisibleProperty();

            }
        };
        Callback<ListView<TileMapLayer>, ListCell<TileMapLayer>> forListView = CheckBoxListCell.forListView(getProperty);

        listView.setCellFactory(forListView);

Resulting in this:

Using a CheckBoxListCell to display the Layers

Now we only need to show some more meaningful text. You can do that by adding a StringConverter. Fortunately the factory method takes a StringConverter as it’s second argument:

layerList = FXCollections.observableArrayList(tileMap.getLayers());
        listView = new ListView<TileMapLayer>();
        listView.setItems(layerList);
        if (!layerList.isEmpty()) {
            listView.getSelectionModel().select(layerList.get(0));
        }
        Callback<TileMapLayer, ObservableValue<Boolean>> getProperty = new Callback<TileMapLayer, ObservableValue<Boolean>>() {
            @Override
            public BooleanProperty call(TileMapLayer layer) {

                return layer.getVisibleProperty();

            }
        };
        StringConverter stringConverter = new StringConverter<TileMapLayer>() {
            @Override
            public String toString(TileMapLayer t) {
                return t.getName();
            }

            @Override
            public TileMapLayer fromString(String string) {
                for (TileMapLayer tileMapLayer : layerList) {
                    if (string.equals(tileMapLayer.getName())) {
                        return tileMapLayer;
                    }
                }
                return null;
            }
        };
        Callback<ListView<TileMapLayer>, ListCell<TileMapLayer>> forListView = CheckBoxListCell.forListView(getProperty, stringConverter);

        listView.setCellFactory(forListView);

That’s it, now the TileMapLayer will be displayed as desired:

 

And here’s a video of the Application in action:

Space Invaders in less than 175 LOC

With the current version of the API I’m at less than 175 LOC for Space Invaders. I’ve got lot’s of “Functional Interfaces” in my APIs that can be converted to Lambda Expressions with JavaFX 8( e.g. SpriteProvider and CollisionHandler). That will make the code nicer and shorter. I could probably also reduce the linecount by bundling the recources (e.g. TileSets) and creating more factories and Builders (SpriteBuilder). But I’m getting closer to what I want…

package de.eppleton.fx2d.samples.spaceinvaders;

import de.eppleton.fx2d.collision.*;
import de.eppleton.fx2d.*;
import de.eppleton.fx2d.action.*;
import de.eppleton.fx2d.tileengine.*;
import java.util.Collection;
import java.util.logging.*;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.canvas.*;
import javafx.scene.input.*;
import javafx.scene.media.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javax.xml.bind.JAXBException;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;

public class SpaceInvaders extends Game {
    
    Points TEN = new Points(10);
    Points TWENTY = new Points(30);
    Points THIRTY = new Points(40);
    DoubleProperty invaderXVelocity = new SimpleDoubleProperty(0.3);
    AudioClip shootSound = new AudioClip(SpaceInvaders.class.getResource("/assets/sound/shoot.wav").toString());
    AudioClip invaderKilledSound = new AudioClip(SpaceInvaders.class.getResource("/assets/sound/invaderkilled.wav").toString());
    MediaPlayer mediaPlayer = new MediaPlayer(new Media(SpaceInvaders.class.getResource("/assets/sound/invader_loop1.mp3").toString()));
    int score = 0;
    String message;
    int[][] enemies = new int[][]{
        {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},
        {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20},
        {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20},
        {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
        {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
    };
    
    @Override
    protected void initGame() {
        final GameCanvas canvas = getCanvas();
        try {
            TileSet invaders = TileMapReader.readSet("/assets/graphics/invaders1.tsx");
            TileSet playerTiles = TileMapReader.readSet("/assets/graphics/player.tsx");
            final TileSetAnimation animation30 = new TileSetAnimation(invaders, new int[]{0, 1}, 2);
            final TileSetAnimation animation10 = new TileSetAnimation(invaders, new int[]{4, 5}, 2);
            final TileSetAnimation animation20 = new TileSetAnimation(invaders, new int[]{2, 3}, 2);
            final TileSetAnimation playerAnimation = new TileSetAnimation(playerTiles, new int[]{0}, 100_000);
            for (int i = 0; i < enemies.length; i++) {
                int[] is = enemies[i];
                for (int j = 0; j < is.length; j++) {
                    Points points = is[j] == 30 ? THIRTY : is[j] == 20 ? TWENTY : TEN;
                    Sprite sprite = new Sprite(canvas, "" + ((j * 11) + i), 50 + (40 * j), 140 + (40 * i), 30, 20, Lookups.fixed(points));
                    sprite.setAnimation(is[j] == 30 ? animation30 : is[j] == 20 ? animation20 : animation10);
                    sprite.setVelocityXProperty(invaderXVelocity);
                }
            }
            Sprite player = new Sprite(canvas, playerAnimation, "Player", 350, 620, 40, 30, Lookup.EMPTY);
            player.setAnimation(playerAnimation);
            player.addAction(KeyCode.LEFT, ActionFactory.createMoveAction(playerAnimation, "left", -4, 0, 0, 0));
            player.addAction(KeyCode.RIGHT, ActionFactory.createMoveAction(playerAnimation, "right", 4, 0, 0, 0));
            player.addAction(KeyCode.UP, new ShootAction(playerAnimation, "fire", new BulletProvider(), new HitHandler(), shootSound));
        } catch (JAXBException ex) {
            Logger.getLogger(SpaceInvaders.class.getName()).log(Level.SEVERE, null, ex);
        }
        canvas.addLayer(new Background());
        canvas.addBehaviour(new MoveInvadersBehavior());
        mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
        mediaPlayer.play();
        canvas.addLayer(new SpriteLayer());
        canvas.start();
    }
    
    @Override
    protected double getViewPortWidth() {
        return 700;
    }
    
    @Override
    protected double getViewPortHeight() {
        return 700;
    }
    
    public static void main(String[] args) {
        launch(args);
    }
    
    private class Points {
        
        int points;
        
        public Points(int points) {
            this.points = points;
        }
        
        public int getPoints() {
            return points;
        }
    }
    
    static class BulletProvider implements SpriteProvider {
        
        @Override
        public Sprite getSprite(GameCanvas parent, double x, double y) {
            return new Sprite(parent, "bullet", x, y + 10, 10, 20, Lookup.EMPTY);
        }
    }
    
    class HitHandler implements CollisionHandler {
        
        @Override
        public void handleCollision(Collision collision) {
            Points points = collision.getSpriteTwo().getLookup().lookup(Points.class);
            if (points != null) {
                score += points.getPoints();
                invaderKilledSound.play();
                collision.getSpriteOne().remove();
                collision.getSpriteTwo().remove();
            }
        }
    }
    
    class MoveInvadersBehavior extends Behavior {
        
        @Override
        public boolean perform(GameCanvas canvas, long nanos) {
            Collection<Sprite> sprites = canvas.getSprites();
            boolean stop = false;
            boolean win = true;
            for (Sprite sprite1 : sprites) {
                if (sprite1.getLookup().lookup(Points.class) != null) {
                    win = false;
                    if (sprite1.getX() > 650 || sprite1.getX() < 50) {
                        invaderXVelocity.set(-invaderXVelocity.doubleValue() * (stop ? 0 : 1.3));
                        if (sprite1.getY() >= 600) {
                            message = "Game Over!";
                            stop = true;
                            mediaPlayer.stop();
                        }
                        for (Sprite sprite2 : sprites) {
                            if (sprite2.getLookup().lookup(Points.class) != null) {
                                sprite2.setY(sprite2.getY() + (stop ? 0 : 20));
                            }
                        }
                        break;
                    }
                }
            }
            if (win) {
                message = "You win!";
                canvas.stop();
                mediaPlayer.stop();
            }
            return true;
        }
    }
    
    class Background extends Layer {        
        
        @Override
        public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
            graphicsContext.setFill(Color.BLACK);
            graphicsContext.fillRect(0, 0, width, height);
            graphicsContext.setFill(Color.WHITE);
            graphicsContext.setFont(Font.font("OCR A Std", 20));
            graphicsContext.fillText("SCORE<1>    HI-SCORE    SCORE<2>", 30, 30);
            graphicsContext.fillText("" + score + "            9990   ", 30, 60);
            graphicsContext.fillText(message, 300, 400);
            graphicsContext.fillText("" + 3 + "                   CREDIT " + 1, 30, 680);
            graphicsContext.setFill(Color.GREEN);
            graphicsContext.fillRect(30, 650, 640, 4);
        }
    }
}

Here’s a video of the game:

JavaFX Kurs mit Toni Epple in München

JavaFX 2 wird die neue Standard-Bibliothek für die Entwicklung von grafischen Benutzeroberflächen in Java. Seit Java SE 7 Update 6 wird JavaFX bereits zusammen mit Oracles Java Laufzeitumgebung ausgeliefert. In diesem Kurs lernen Sie, wie Sie mit JavaFX grafisch ansprechende Applikationen entwickeln können und welche Features und Tools Ihnen dazu zur Verfügung stehen.

Dieser Kurs ist so konzipiert, dass Sie schnell einen Überblick über die gesamte JavaFX-API erhalten. Schritt für Schritt erfahren Sie, wie man eine erste Anwendung baut, wie man das eigene Datenmodell in der Oberfläche darstellt und editierbar macht und wie man die Anwendung mit Graphen, Animationen, Audio und Video anreichert, um ein modernes ansprechendes UI zu erhalten.  Ein besonderes Augenmerk gilt den vorhandenen Tools, die bei der Entwicklung und beim Debuggen unterstützen.

Der Kurs richtet sich gleichermaßen an Einsteiger und Umsteiger. Swing-Entwickler, die das neue Standard-UI erlernen möchten, erfahren, welche Änderungen das Arbeiten mit einem SceneGraph mit sich bringt, und wie sie Anwendungen schrittweise mit JavaFX anreichern und schließlich portieren können. Java-Entwickler, die erst in die GUI-Entwicklung einsteigen, lernen von Grund auf, wie man moderne Benutzerüberflächen  mithilfe von JavaFX entwickelt

weitere Infos unter: eppleton.de

A minimal JavaFX Presentation (in JavaFX)

If you want to do a presentation about JavaFX, it’s very handy to do the presentation itself in JavaFX. This way you can easily show your examples without leaving the presentation. Here’s a very minimal example how to do that.

In NetBeans set up a new JavaFX Project “New Project” -> “JavaFX” -> “JavaFX Application” and name it “FXPresenter”. Now create the Slide class. It’s used to load an FXML file:

package fxpresenter;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;

public class Slide extends AnchorPane implements Initializable {
     
    public Slide(String slide) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(slide));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }
}

Next we need a Presentation that contains the slides and switches between them:

package fxpresenter;

import java.util.ArrayList;
import java.util.List;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Screen;

public class Presentation extends Group {

    private List<Slide> slides;
    private int index;
    private Slide current;
    public EventHandler<KeyEvent> keyEventHandler;

    public Presentation() {
        this.slides = new ArrayList<>();
        keyEventHandler = new EventHandler<KeyEvent>() {
            public void handle(final KeyEvent keyEvent) {
                if (keyEvent.getCode() == KeyCode.LEFT) {
                    previousSlidePlease();
                } else if (keyEvent.getCode() == KeyCode.RIGHT) {
                    nextSlidePlease();
                }
            }
        };
    }

    public void addSlide(Slide slide) {
        addSlide(slides.size(), slide);
    }

    public void addSlide(int index, Slide slide) {
        slides.add(index, slide);
    }

    public void previousSlidePlease() {
        if (index > 0) {
            index--;
        }
        setSlide(index);
    }

    public void nextSlidePlease() {
        if (index < slides.size() - 1) {
            index++;
        }
        setSlide(index);
    }

    public void setSlide(int index) {
        if (current != null) {
            getChildren().remove(current);
            current.removeEventHandler(KeyEvent.KEY_PRESSED, keyEventHandler);
        }
        current = slides.get(index);
        current.addEventHandler(KeyEvent.KEY_PRESSED, keyEventHandler);

        scaleToFit();
        getChildren().add(slides.get(index));
        current.requestFocus();
    }

    void start() {
        index = -1;
        nextSlidePlease();
    }

    private void scaleToFit() {
        javafx.geometry.Rectangle2D screenBounds = Screen.getPrimary().getBounds();
        double prefWidth = current.getPrefWidth();
        double prefHeight = current.getPrefHeight();
        double scaleX = screenBounds.getWidth() / prefWidth;
        double scaleY = screenBounds.getHeight() / prefHeight;
        double centerX = (screenBounds.getWidth() / 2) - (prefWidth / 2);
        double centerY = (screenBounds.getHeight() / 2) - (prefHeight / 2);
        setTranslateX(centerX);
        setTranslateY(centerY);
        setScaleX(scaleX);
        setScaleY(scaleY);
    }
}

It should be pretty obvious what the code does: If you set a slide it will be scaled to fit the screen, and it will listen to key events. pressing the right arrow moves to the next slide, and the right arrow key set’s the previous slide.
And finally we need an Application to display the whole stuff in full screen:

package fxpresenter;


import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class FXPresenter extends Application {

    @Override
    public void start(Stage stage) throws Exception {
      
        final Presentation presentation = new Presentation();
        presentation.addSlide(new Slide("Slide1.fxml"));
        presentation.addSlide(new Slide("Slide2.fxml"));
      
        final Scene scene = new Scene(presentation);
        stage.setScene(scene);
        stage.setFullScreen(true);
        presentation.start();
        List<Screen> screens = Screen.getScreens();
        
        stage.show();
    }

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

}

You’re done. Now you can go ahead and create your slides. For the sample create two new FXML files named “Slide1.fxml” and “Slide2.fxml”, and style them using the SceneBuilder, and your ready to go. Here’s a little video showing how you can use SceneBuilder to create the Slides:

Pong! in less than 80 LOC

My idea for the JavaFX game engine is to create an API that makes coding games really easy. So my first goal was to be able to create a simple game like Pong! in less than 100 lines of code. So after creating the API I optimized it a bit and voilà this is Pong in less than 100 LOC (including imports and some empty lines):

import de.eppleton.fx2d.*;
import de.eppleton.fx2d.action.*;
import de.eppleton.fx2d.physics.*;
import de.eppleton.fx2d.physics.action.PhysicsActionFactory;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.text.Font;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.*;
import org.openide.util.Lookup;

public class Pong extends Game {

    int scorePlayer, scoreComputer = 0;

    @Override
    protected void initGame() {
        GameCanvas canvas = getCanvas();
        final PhysicsEngine physicsEngine = new PhysicsEngine(new Vec2(0, 0), new Vec2(0, 6), 100, canvas, true);
        canvas.addLayer(new Layer() {
            @Override
            public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
                graphicsContext.setFont(Font.font("Verdana", 36));
                graphicsContext.fillText("" + scorePlayer + "   " + scoreComputer, 380, 60);
            }
        });
        physicsEngine.createWall(0, 580, 800, 10);
        physicsEngine.createWall(0, 10, 800, 10);
        addBall(canvas, physicsEngine, 400, 300);
        canvas.addBehaviour(new Action() {
            @Override
            public boolean perform(Sprite sprite, GameCanvas playingField) {
                Sprite sprite1 = playingField.getSprite("ball");
                if (sprite1 != null) {
                    Body ball = sprite1.getLookup().lookup(Body.class);
                    if (ball.getPosition().x <= 0) {
                        scoreComputer++;
                        physicsEngine.remove(ball);
                        addBall(playingField, physicsEngine, 400, 300);
                    } else if (ball.getPosition().x >= 8 ) {
                        scorePlayer++;
                        physicsEngine.remove(ball);
                        addBall(playingField, physicsEngine, 400, 300);
                    }
                }
                return true;
            }
        });
        Sprite bat = new Sprite(canvas, "Player", 10, 262, 30, 75, Lookup.EMPTY);
        physicsEngine.createDefaultBody(bat, BodyType.KINEMATIC, 1, .4f, 0);
        bat.addAction(KeyCode.A, PhysicsActionFactory.createLinearMoveAction(null, "up", new Vec2(0, 4), new Vec2(0, 0)));
        bat.addAction(KeyCode.Z, PhysicsActionFactory.createLinearMoveAction(null, "down", new Vec2(0, -4), new Vec2(0, 0)));
        Sprite computer = new Sprite(canvas, "Computer", 750, 262, 30, 75, Lookup.EMPTY);
        physicsEngine.createDefaultBody(computer, BodyType.KINEMATIC, 1, .3f, 0);
        computer.addBehaviour(new Action() {
            @Override
            public boolean perform(Sprite sprite, GameCanvas playingField) {
                Body me = sprite.getLookup().lookup(Body.class);
                Sprite ball = playingField.getSprite("ball");
                if (ball != null) {
                    Body lookup = ball.getLookup().lookup(Body.class);
                    me.setLinearVelocity(lookup.getPosition().y > me.getPosition().y
                            ? new Vec2(0, -1.5f) : new Vec2(0, 1.5f));
                }
                return true;
            }
        });
    }

    private void addBall(GameCanvas canvas, PhysicsEngine physicsEngine, int x, int y) {
        Sprite ball = new Sprite(canvas, "ball", x, y, 20, 20, Lookup.EMPTY);
        Body ballBody = physicsEngine.createDefaultBody(ball, BodyType.DYNAMIC, 1, .4f, .2f, true);
        ballBody.setLinearVelocity(new Vec2(4, 1));
    }

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

JavaFX Game Engine now with Physics

I’ve just added a pluggable physics engine to my game engine. So now there are three modules: the core game engine that allows for the creation of simple canvas based games, and an optional tile engine that can read TMX files and render TileBased Worlds and Sprite animations, as well as a pluggable physics Engine that you can use to add realistic physics. Using the NetBeans Lookups API it’s really simple to make it pluggable. Ah, and there’s a fourth module with demos.

The Physics API is still very simple, but you can already do some nice stuff. A nice goodie is the DebugLayer that you can add to the GameCanvas to render the JBox2D bodies. This way you can easily test and debug a game and find bugs with custom Sprite rendering. I’m also trying to provide some helper methods for mapping the Bodies and Shapes to the Sprites, since that’s sometimes confusing…

The first game I created is the “Hello World!” of Games, Pong! Here you can get an impression of the current API:

Canvas for Bck2Brwsr

Almost two weeks ago I met Jaroslav Tulach in Linz for our annual NetBeans Platform certified Training at the Johanes-Kepler-University in Linz. Every year we take some time to talk about projects and do some coding over a couple of beers, producing “Barcode” if you will. Last year we created a dynamic upload center for NetBeans Platform applications. This year Jarda showed me his new project Bck2Brwsr. The goal of the project is to bring Java back to the Browser. Java has lost a lot of ground in that area, and the advent of smartphones and tablets speeds up this trend, since it’s getting harder and harder to set up the environment for executing Java applications or applets.

In the meantime JavaScript is delivering on the WORA promise Java once started with. And while Java is great as a language especially for larger projects, for structuring and modularizing code in order to create maintainable projects, the platform has lost a lot of ground on the devices. Developers like the language, the superior IDE support as compared to JavaScript, the compile time checks, but for Browser based Projects, Java is no longer a choice as client technology.

The main idea of Bck2Brwsr is to change that and make it dead simple to execute Java Code in the Browser on any device. Since JavaScript is ubiquitous, it’s the obvious target language. GWT solves the problem by providing some APIs and a compiler that generates JavaScript. Bck2Brwsr is different in that it creates a JVM that is written in JavaScript and is capable of executing Java Bytecode directly. So when a Java runtime is available, the code could be executed by that, and benefit from the superior performance of the JVM. But if there’s no JVM, a minimal environment is automatically installed and runs the code in the Bck2Brwsr VM. And if you wonder how such a system will perform, I found Bck2Brwsr to be surprisingly fast already.

Another difference to GWT is, that there’s no standard API for widgets. The idea is, that you as a developer can very easily create your own APIs. For my current projects that would be excellent. I’ve created a GameEngine that is using JavaFX Canvas to render TileMaps and Sprites, and my plan was to create a JavaScript/ HTML5 Canvas version of that as well, so I can target any device. Over a couple of Austrian beers Jarda showed me how to implement such an API, and yesterday I found a little time to code so I gave it a try.

The final plan is to recreate the JavaFX Canvas API to render to HTML5 Canvas. But there are some differences, e.g. how the individual functions like arc and arcTo work. The bigger differences are additional features of JavaFX Canvas like the ability to use SVG paths. All of that should be no big problem, but require some work. So I decide to first implement a 1:1 Java version of the HTML5 Canvas API.

Time to head over to w3schools and have a look at Canvas. There are several subsections. The first one is ”Colors, Styles, and Shadows”. Here we have a couple of properties we can read or set on the GraphicsContect of the Canvas, like fillStyle, strokeStyle, etc.. So what we first need to do is create a representation of the Canvas Element and it’s GraphicsContext:


public class Canvas extends Element {

public Canvas(String id) {
 super(id);
 }

// code omitted

@JavaScriptBody(
 args = {"el"},
 body = "var e = window.document.getElementById(el.fld_id);\n"
 + "return e.getContext('2d');\n")
 private native static Object getContextImpl(Canvas el);

public GraphicsContext getContext() {
 return new GraphicsContext(getContextImpl(this));
 }

We will also need to register our Element with Bck2Brwsr’s PageProcessor. Now when the PageProcessor hits a Canvas Element on the Web page, it will create a Canvas Element for us. The @JavaScriptBody annotation can be used to directly generate the native JavaScript Code for us. In getContextImpl it will find the current Canvas Object and call a method on it to give us the GraphicsContext. We then create a Java Object Wrapper around that JavaScript Object:


public class GraphicsContext {

Object context;

GraphicsContext(Object contextImpl) {
 this.context = contextImpl;
}

@JavaScriptBody(args = {"style"}, body = "this.fld_context.fillStyle=style;")
 public native void setFillStyle(String style);

 @JavaScriptBody(args = {}, body = "return this.fld_context.fillStyle;")
 public native String getFillStyle();

// code omitted

}

So the getter/setter style for simple types is fairly simple to implement. It get’s slightly more complicated when dealing with Objects. HTML5 Canvas also allows us to set Gradients or Patterns as fills. These are JavaScript Objects, so we need some Wrappers to represent them in our Java Code, and we need to provide a reference to be used by the native code:


public void setFillStyle(LinearGradient style) {
 setFillStyleImpl(style.object());
 }

 public void setFillStyle(RadialGradient style) {
 setFillStyleImpl(style.object());
 }

 public void setFillStyle(Pattern style) {
 setFillStyleImpl(style.object());
 }

 @JavaScriptBody(args = {"obj"}, body = "this.fld_context.fillStyle=obj;")
 private native void setFillStyleImpl(Object obj);

The object() method just returns the wrapped JavaScript Object reference:


public class LinearGradient {

private final Object gradient;

LinearGradient(Object linearGradient) {
 this.gradient = linearGradient;
 }

Object object() {
 return gradient;
 }

 public void addColorStop(double position, String color) {
 addColorStopImpl(gradient, position, color);
 }

@JavaScriptBody(args = {"gradient", "position", "color"}, body =
 "gradient.addColorStop(position,color)")
 private native void addColorStopImpl(Object gradient, double position, String color);

}

The rest is applying this pattern to all API methods, so we get a simple 1:1 mapping to Java. And then we can use it in Java Code like this:


/**
 * Edit the index.xhtml file. Use 'id' to name certain HTML elements. Use this
 * class to define behavior of the elements.
 */
@Page(xhtml = "index.xhtml", className = "Index")
public class App {

@On(event = CLICK, id = "hello")
 static void hello() {
 GraphicsContext context = Index.MYCANVAS.getContext();
 // Paths
 context.beginPath();
 context.moveTo(100, 20);
 context.lineTo(200, 20);
 context.arcTo(300, 300, 50, 50, 40);
 context.setFillStyle("#ff0000");
 context.setShadowColor("#000000");
 context.setShadowOffsetX(10);
 context.setShadowOffsetY(10);
 context.setShadowBlur(20);
 context.fill();

 // TextMetrics
 context.fillText("width: " + context.measureText("Hallo").getWidth(), 10, 10);

 // Gradients
 LinearGradient grd = context.createLinearGradient(0, 0, 170, 0);
 grd.addColorStop(0, "black");
 grd.addColorStop(0.3, "magenta");
 grd.addColorStop(0.5, "blue");
 grd.addColorStop(0.6, "green");
 grd.addColorStop(0.8, "yellow");
 grd.addColorStop(1, "red");
 context.setFillStyle(grd);
 context.fillRect(10, 200, 200, 200);

 // Pixel Manipulation
 ImageData imageData = context.getImageData(100, 0, 100, 100);
 context.putImageData(imageData, 200, 200);

 // Image
 context.drawImage(Index.SCREAM, 1, 1, 100, 100);

 // Fonts
 context.setFont(
 "20px Georgia");
 context.strokeText("Hello World!", 500, 50);
 context.setFont(
 "30px Verdana");
 LinearGradient gradient = context.createLinearGradient(0, 0, 800, 0);
 gradient.addColorStop(0, "magenta");
 gradient.addColorStop(0.5, "blue");
 gradient.addColorStop(1.0, "red");
 context.setStrokeStyle(gradient);
 context.strokeText("Big smile!", 500, 90);
 }
}

The code doesn’t look to different to JavaFX Canvas, does it? So here’s the ugly result :-):

It’s really easy and straightforward to create an API like that, and I’m looking forward to do some more coding with Bck2Brwsr!

Writing a Tile Engine in JavaFX

With the advent of embedded versions of JavaFX, our framework has become more interesting for game development, since we now can target small consumer devices like tablets & smartphones. So I decided to do a little more experimenting with JavaFX for writing Games. This time I wanted to use Canvas to have more control over the rendering in order to be able to optimize the performance on smaller devices. These are my experiences when writing a Tile Engine.

What’s a Tile Engine?

Back in the early days game consoles & computers had very limited resources. So in order to have games with thousands of large screens developers needed to come up with a method to store the screens in a format other than a bitmap per screen. So Tile Engines were invented that can generate large screens from a limited set of re-usable smaller graphics (Tiles). This saves ram and improves rendering performance.

TileMaps

The instructions how to generate the screen are stored in TileMaps. Those maps are typically organized as a 2-dimensional matrix of Tile ids. Usually the tiles are organized in layers to allow for a simple Z-ordering and more flexibility in combining graphics with different backgrounds. Usually TileMaps also support storing of meta data, for example if certain tiles are blocked, or spawn points for enemies.

A TileMap with several layers created with the

TileSets

The tiles referenced in the map are usually stored in TileSets that consist of a single bitmap and meta information about how to divide it into tiles. Here’s an example of such an image from opengameart.com, a site that hosts game assets with Open Source Licences. In my examples I use some of these graphics.

A typical TileSet Image sized 1024 x 1024 (^2 = good for graphics cards)

ObjectGroups

One additional feature of the TMX format are Object Layers. These special layers can be used to define freeform shapes and polylines and assign properties to them. The basic idea behind that is that we can use them to define areas where Sprites are created (spawnpoints), exits, portals, and non-rectangular collision shapes. It’s up to the creator of the TileEngine or the developer who builds games with it to define how to handle the ObjectGroups. I’m planning to use them extensively, and they are a very nice extension point for declaratively defining the gameplay. You can e.g. use them to define animations, skript dialogs, etc..

Spawnpoints defined in the

Workflow, Tools & Formats

The idea of tilemaps also allows for a nice workflow. Graphic designers can create the assets and game designers can import them into a level editor like “Tiled” and design the levels via drag & drop. The maps are stored in a machine readable TileMap format. Tiled for example uses the TMX Map format for storing the TileMap. That’s a very simple XML format, that can then be loaded by the TileEngine. For my implementation I decided to use the TMX Format, so I can use “Tiled” for designing the levels.

Implementation in JavaFX

For the implementation I decided to use JavaFX Canvas immediate mode rendering instead of the retained mode rendering when using individual Nodes. This gives me a bit more control for optimizing the performance on small devices like the Raspberry Pi.

Reading TMX/TSX files

The first thing we need is a way to read the TileMap (TMX) and TileSet (TSX) files. With JAXB it’s pretty simple to create a TileMapReader that can create POJOs from a file. So if you use the Engine you simply call:

TileMap map = TileMapReader.readMap(“path/to/my/map.tmx”);

The Camera

Since in most games the TileMaps will be larger than the screen, only a part of the Map is rendered. Usually the map is centered on the hero. You can do that by simply tracking the map position of upper left corner of the screen. We refer to this as our Camera position. The position is then updated from the hero’s position just before the TileMap is rendered like this:

// the center of the screen is the preferred location of our hero

double centerX = screenWidth / 2;

double centerY = screenHeight / 2;

cameraX = hero.getX() - centerX;

cameraY = hero.getY() - centerY;

We just need to make sure the camera doesn’t leave the tilemap:

// if we get too close to the borders

if (cameraX >= cameraMaxX) {

cameraX = cameraMaxX;

}

if (cameraY >= cameraMaxY) {

cameraY = cameraMaxY;

}

Rendering the TileMap using Canvas

Then it’s really easy to render the tiles. we simply loop through the layers and ask the tilemap to render the correct image at the current position. First we need to find out which tiles are currently visible, and the offset, since our hero moves pixel by pixel, not tile by tile:

// x,y index of first tile to be shown

int startX = (int) (cameraX / tileWidth);

int startY = (int) (cameraY / tileHeight);

// the offset in pixels

int offX = (int) (cameraX % tileWidth);

int offY = (int) (cameraY % tileHeight);

Then we loop through the visible layers and draw the tile:

for (int y = 0; y < screenHeightInTiles; y++) {

for (int x = 0; x < screenWidthInTiles; x++) {

// get the tile id of the tile at this position

int gid = layer.getGid((x + startX) + ((y + startY) * tileMap.getWidth()));

graphicsContext2D.save();

// position the graphicscontext for drawing

graphicsContext2D.translate((x * tileWidth) - offX, (y * tileHeight) - offY);

// ask the tilemap to draw the tile

tileMap.drawTile(graphicsContext2D, gid);

// restore the old state

graphicsContext2D.restore();

}

}

The TileMap will then find out which Tileset the Tile belongs to and ask the TileSet to draw it to the Context. Drawing itself is as simple as finding the correct coordinates in your TileSets Image:

public void drawTile(GraphicsContext graphicsContext2D, int tileIndex) {

int x = tileIndex % cols;

int y = tileIndex / cols;

// TODO support for margin and spacing

graphicsContext2D.drawImage(tileImage, x * tilewidth, y* tileheight, tilewidth, tileheight, 0, 0, tilewidth, tileheight);

}

Game Loop. So we can simplify it to:

The Game Loop is again very simple. I’m using a TimeLine and a KeyFrame to fire a pulse for the game at a certain framerate (FPS):

final Duration oneFrameAmt = Duration.millis(1000 / FPS);

final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,

new EventHandler() {

@Override

public void handle(Event t) {

update();

render();

}

});

TimelineBuilder.create()

.cycleCount(Animation.INDEFINITE)

.keyFrames(oneFrame)

.build()

.play();

Sprites

Every call to update in the TileMapCanvas loops through all Sprites and updates them. Basic Sprites currently contain one TileSet with a walkcycle like this:

Since sprites typically have a lot of transparent space around them, in order to have some extra room for animated behavior like like swinging a sword, I decided to allow to add a MoveBox and a CollisionBox for convenience. The CollisionBox can be used to define an area where our hero can be hurt. The MoveBox should be placed around the legs, so it can pass in front of forbidden tiles while the upper body is overlapping the tile. The blueish area around our “hero” is the sprite boundary:

Sprites can also have a timed Behavior. On every update the Sprite loops through it’s behaviors and checks if it’s time to fire. If so it’s “behave” method is called. If we have an enemy, like the skeleton in the sample app, we can add it’s AI here. Our Skeleton has for example a very simple behavior to make it follow our hero. It also checks for collision and causes damage to our hero like that:

monsterSprite.addBehaviour(new Sprite.Behavior() {

@Override

public void behave(Sprite sprite, TileMapCanvas playingField) {

if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {

hero.hurt(1);

}

}

});

The default interval is a second. If you need other intervals you can set them. Behaviors are reusable, different sprites can share the same Behavior instance. Behaviors are similar to KeyFrames, and I’m currently also using them to time the Animations (increase the tile index for the next render call).

ObjectGroupHandler

As mentioned in the beginning ObjectGroups are handy extension points. In my example game I use them for defining the spawn points of our hero and the monsters. Currently you simply add an ObjectGroupHandler which in turn uses the information in the ObjectGroup to create the Hero and Monster sprites and add Behavior to them:

class MonsterHandler implements ObjectGroupHandler {

Sprite hero;

@Override

public void handle(ObjectGroup group, final TileMapCanvas field) {

if (group.getName().equals("sprites")) {

for (TObject tObject : group.getObjectLIst()) {

if (tObject.getName().equals("MonsterSpawner")) {

try {

double x = tObject.getX();

double y = tObject.getY();

TileSet monster = TileMapReader.readSet("/de/eppleton/tileengine/resources/maps/BODY_skeleton.tsx");

Sprite monsterSprite = new Sprite(monster, 9, x, y, "monster");

monsterSprite.setMoveBox(new Rectangle2D(18, 42, 28, 20));

field.addSprite(monsterSprite);

monsterSprite.addBehaviour(new Sprite.Behavior() {

@Override

public void behave(Sprite sprite, TileMapCanvas playingField) {

if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {

hero.hurt(1);

}

}

});

}

Putting it all together

To create a sample game all you need to do is create TileMaps, TileSets, one or more ObjectGroupHandler(s) to create the Sprites and add Behavior, and you’re ready to play:

// create the world

TileMap tileMap = TileMapReader.readMap("/de/eppleton/tileengine/resources/maps/sample.tmx");

// initialize the TileMapCanvas

TileMapCanvas playingField = new TileMapCanvas(tileMap, 0, 0, 500, 500);

// add Handlers, can also be done declaratively.

playingField.addObjectGroupHandler(new MonsterHandler());

// display the TileMapCanvas

StackPane root = new StackPane();

root.getChildren().add(playingField);

Scene scene = new Scene(root, 500, 500);

playingField.requestFocus();

primaryStage.setTitle("Tile Engine Sample");

primaryStage.setScene(scene);

primaryStage.show();

That was the starting point of my Tile Engine. In the meantime it has evolved a bit into a more general purpose 2D-engine, so also Sprites that are not using TileSets and Layers that are freely rendered are supported. But it works pretty well so far.

Angry Nerds – JavaOne Edition

Current version of Angry Nerds as shown at JavaOne now with levelboard, animated bugs and score:

I had to slow down the game a bit to be closer to the original game. The next step will be to add a level editor… Looking forward to show the next version at my W-JAX session: Angry Duke – Physics Games mit JavaFX

…maybe even running on the PandaBoard I’ve just ordered.

 

 

The Duke is angry…

… somebody has stolen all his JavaBeans. Get out your keyboard and slingshot at JavaOne, and launch Duke at his mean, green enemies :-).

These are my sessions for JavaOne 2012:

Session ID: CON3826
Session Title: Patterns for Modularity: What Modules Don’t Want You to Know

Session ID: CON5784
Session Title: Rich Clients with JavaFX and the NetBeans Platform

Session ID: TUT5729
Session Title: Angry Duke: Physics-Based Games in JavaFX

I’m especially exited about the new tutorial session format, where we’ll have enough time to do plenty of life coding and do a real JavaFX implementation of “Angry Duke” also featuring some Angry Nerds :-).