NetBeans 7.4 with support for Cordova

Cordova is a really interesting project for creating native Android and iOS apps using just JavaScript HTML5 and CSS. The latest NetBeans release adds support for it. Here’s a screencast showing how to use it in your projects:

Since JavaScript is not my cup of coffee, you might be wondering why I’m mentioning it. The reason is, that you could also use that support to package bck2brwsr applications with it. And since there is now support for JavaFX in bck2brwsr it’s another way to create native JavaFX applications for these platforms :-).

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.

Das Chuck Norris Experiment – Java ohne Jailbreak auf iPad und Android

Chuck Norris kann Java auf dem iPad starten – ohne jailbreak!” Mal sehen was nötig ist um das ohne Roundhouse-Kick selbst nachzuvollziehen. Hier wird bck2brwsr vorgestellt, ein neues Open Source Projekt, das es ermöglicht Java direkt im Browser laufen zu lassen – ohne Java Plugin. Anders als bei GWT und ähnlichen Projekten wird der Quellcode nicht nach JavaScript kompiliert, sondern als ByteCode in einer JavaScript basierten JVM ausgeführt. Als Demo bauen wir eine Spiele-App, die auf Android und iOS genauso läuft wie auf dem Desktop. Und all das ist keine Zukunftsmusik, sondern bereits verfügbar und kann sofort eingesetzt werden…

Simple hierarchical popupmenu for Node

I recently got a question how you can organize your Node’s popup menus hierarchically, while keeping registration declarative. Organizing your actions hierachically in the Layer and then using Lookups.forPath().lookupAll(Action.class) doesn’t work, the result is a flat list. So here’s the simplest version I know:

AbstractNode example = new AbstractNode(Children.LEAF) {
            @Override
            public Action[] getActions(boolean context) {
                return findActionsForFolder(FileUtil.getConfigFile("MYPATH"));
            }

            protected Action[] findActionsForFolder(FileObject folder) {
                ArrayList actions = new ArrayList();
                if (folder != null && folder.isFolder()) {

                FileObject[] children = folder.getChildren();
                for (FileObject fileObject : children) {
                    if (fileObject.isFolder()) {
                        Action[] findActionsForFolder = findActionsForFolder(fileObject);
                        Action menu = new MenuAction(fileObject.getName(), findActionsForFolder);
                        actions.add(menu);
                    } else {
                        try {
                            Action action = (Action) DataObject.find(fileObject).getLookup().lookup(InstanceCookie.class).instanceCreate();
                            actions.add(action);
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        } catch (ClassNotFoundException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                }
}
                return actions.toArray(new Action[actions.size()]);
            }
        };

I’ll leave it as an exercise for you to change the code to use InstanceCookie.Of…
And here’s the MenuNode:

class MenuAction extends AbstractAction implements Presenter.Popup, Presenter.Menu {

Action[] actions;
String name;
public JMenu menu;

public MenuAction(String name, Action[] findActionsForFolder) {
super(name);
this.name = name;
this.actions = findActionsForFolder;
this.menu = new JMenu(name);
for (Action action : actions) {
if (action instanceof Presenter.Popup) {
menu.add(((Presenter.Popup)action).getPopupPresenter());
} else {
menu.add(action);
}

}
}

@Override
public void actionPerformed(ActionEvent e) {
}

@Override
public JMenuItem getPopupPresenter() {

return menu;
}

@Override
public JMenuItem getMenuPresenter() {
return menu;
}
}


That’s it. Now register your actions under MYPATH in hierarchies, e.g.:

@ActionID(
category = "Build",
id = "asd.SomeAction")
@ActionRegistration(
displayName = "#CTL_SomeAction")
@ActionReference(path = "MYPATH/mysubcategory/usd", position = 0)
@Messages("CTL_SomeAction=asd")
public final class SomeAction implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
}
}

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:

Custom Code Templates with NetBeans

In the last weeks I’ve shown you some of the NetBeans Code Templates. The existing ones are nice, but what is even nicer is, that you can easily create your own Java templates. We can for example create the “2no” template I used to get the Node delegate from a DataObject as shown here:

Here’s how:

Go to “Settings“-> “Editor” -> “Code Templates” and select “Java” as the Language:

Now click the “New” Button, a dialog will ask for an abbreviation, enter “2no“. The abbreviation will be added to the table, and you can edit the “Expanded Text“. Add this:


${nodeType type="org.openide.nodes.Node" default="Node" editable="false"} ${node newVarName default="n"} = ${dob instanceof="org.openide.loaders.DataObject" default="dob"}.getNodeDelegate();
${cursor}

The type definition in the beginning will automatically add the import for org.openide.nodes.Node. The default defines, that we will get “Node” as the first String, and it’s not editable by the user. Then we define a new variable with a default name of  ”n“. If that name isn’t available in our scope NetBeans will check for the next free name by adding an int to the String (e.g. “n1“).  Next we declare, that we want to call a method called “getNodeDelegate()” on a “org.openide.datasystems.DataObject“, and if there is none in the scope use “dob” instead.