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:
 
 
 
 
 
 
 
 
Bildschirmfoto-2013-08-06-um-17.41.17

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:

Bildschirmfoto-2013-08-07-um-08.12.45

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:

Bildschirmfoto-2013-08-07-um-08.05.59

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

Bildschirmfoto-2013-08-07-um-08.10.43

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

Bildschirmfoto-2013-08-06-um-17.41.271

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.
 

Reference: Tower Defense in JavaFX (2) from our JCG partner Toni Epple at the Eppleton blog.
Related Whitepaper:

Java Essential Training

Author David Gassner explores Java SE (Standard Edition), the language used to build mobile apps for Android devices, enterprise server applications, and more!

The course demonstrates how to install both Java and the Eclipse IDE and dives into the particulars of programming. The course also explains the fundamentals of Java, from creating simple variables, assigning values, and declaring methods to working with strings, arrays, and subclasses; reading and writing to text files; and implementing object oriented programming concepts. Exercise files are included with the course.

Get it Now!  

Leave a Reply


× eight = 8



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books