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.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


+ three = 4



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
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.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close