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:

https://www.youtube.com/watch?v=08H6LZkcqXw

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 :-) .

 

New NetBeans Plugin: SVG 2 FXML

Update: It seems that due to a backward incompatible change in the JDK the stylesheet cannot load EL-Extensions any more and causes an Exception. I’ve updated the code example to show how to work around it using reflection and the plugin. If you’ve got the old version, please uninstall and install the newer one…

I’ve created a simple plugin for NetBeans 7.1.2 and above that converts SVG to FXML. It uses the stylesheet I mentioned in earlier posts. Conversion isn’t perfect, but it should work ok for files created by Inkscape. If you’ve got files that don’t work, please send them to me so I can improve the plugin.

You can download the Plugin by following this link.

To use it simply right-click on a SVG-File and select “Convert to FXML”. Here’s a 8 second video clip of it in action:

The automatic opening in SceneBuilder is a feature available in NetBeans 7.2, I’m using a development build. In 7.1.2 it will open in the Editor instead.

If you’re interested in doing something similar: I’ve registered an SVG-Filetype for this, and added an action to it’s context menu: The Action itself is quite simple:

</pre>
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.OutputStream;

import java.lang.reflect.Field;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.ErrorManager;
import org.openide.loaders.DataObject;

import org.openide.awt.ActionRegistration;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionID;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.nodes.Node;
import org.openide.util.ContextAwareAction;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.util.RequestProcessor;
import org.openide.util.TaskListener;

@ActionID(category = "File",
id = "de.eppleton.modules.javafx.svg.ConvertToFXMLAction")
@ActionRegistration(displayName = "#CTL_ConvertToFXMLAction")
@ActionReferences({
 @ActionReference(path = "Loaders/text/svg+xml/Actions", position = 160, separatorBefore = 150),
 @ActionReference(path = "Editors/text/svg+xml/Popup", position = 190)
})
@Messages("CTL_ConvertToFXMLAction=Convert to FXML...")
public final class ConvertToFXMLAction implements ActionListener {

private final DataObject context;
 private final static RequestProcessor RP = new RequestProcessor("SVG to FXML Conversion Task", 1, true);

public ConvertToFXMLAction(DataObject context) {
 this.context = context;
 }

public void actionPerformed(ActionEvent ev) {
 Runnable runnable = new Runnable() {
 public void run() {
 try {
 saveBeforeTransformation(context);
 FileObject parent = context.getPrimaryFile().getParent();
 FileObject transformed = null;
try {
 String findFreeFileName = FileUtil.
 findFreeFileName(parent, context.
 getPrimaryFile().getName(), "fxml");
 transformed = parent.
 createData(findFreeFileName, "fxml");
 } catch (IOException ex) {
 try { // try again for the rare coinncidence of someone stealing our filename
 String findFreeFileName = FileUtil.
 findFreeFileName(parent, context.
 getPrimaryFile().getName(), "fxml");
 transformed = parent.
 createData(findFreeFileName, "fxml");
 } catch (IOException ex1) { // give up
 Exceptions.printStackTrace(ex1);
 }
 }
 if (transformed == null) {
 return;
 }

Source xmlSource = new StreamSource(context.getPrimaryFile().
 getInputStream());

Source xsltSource = new StreamSource(ConvertToFXMLAction.class.
 getResourceAsStream("svg2fxml.xsl"));

TransformerFactory transFact =
 TransformerFactory.newInstance();
try {
Field _isNotSecureProcessing = transFact.getClass().getDeclaredField("_isNotSecureProcessing");
_isNotSecureProcessing.setAccessible(true);
_isNotSecureProcessing.set(transFact, Boolean.TRUE);
} catch (Exception x) {
}
Transformer trans = transFact.newTransformer(xsltSource);
 OutputStream outputStream = transformed.getOutputStream();
 trans.transform(xmlSource, new StreamResult(outputStream));
 outputStream.flush();
 outputStream.close();
 final Node node = DataObject.find(transformed).
 getNodeDelegate();
 SwingUtilities.invokeLater(new Runnable() {
 @Override
 public void run() {
 try {
 callAction(node.getPreferredAction(),node,new ActionEvent(node, ActionEvent.ACTION_PERFORMED, "")); // NOI18N
 } catch (Exception ex) {
 ex.printStackTrace();
 }
 }
 });

} catch (IOException ex) {
 Exceptions.printStackTrace(ex);
 } catch (TransformerException ex) {
 Exceptions.printStackTrace(ex);
 }

}
 };

final RequestProcessor.Task theTask = RequestProcessor.getDefault().
 create(runnable);

final ProgressHandle ph = ProgressHandleFactory.
 createHandle("Converting to FXML...", theTask);
 theTask.addTaskListener(new TaskListener() {
 public void taskFinished(org.openide.util.Task task) {
 ph.finish();
 }
 });

ph.start();

theTask.schedule(0);

}

private void saveBeforeTransformation(DataObject dObject) {
 if (dObject.isModified()) {
 SaveCookie save;
 save = (SaveCookie) dObject.getCookie(SaveCookie.class);
 if (save != null) {
 try {
 save.save();
 } catch (IOException ex) {
 ErrorManager.getDefault().
 notify(ErrorManager.INFORMATIONAL, ex);
 }
 }
 }

 }
 private static void callAction(Action a, Node node, ActionEvent actionEvent) {
 if (a instanceof ContextAwareAction) {
 a = ((ContextAwareAction)a).createContextAwareInstance(node.getLookup());
 }
 if (a == null) {
 return;
 }
 a.actionPerformed(actionEvent);
 }
}

Enjoy, and let me know if it works for you!

SVG to FXML using NetBeans

In a recent article I mentioned that I created an XSLT stylesheet for transforming SVG to FXML. I did that because besides there already is a great tool of this in e(FX)clipse, it isn’t portable enough for me. I wanted a stylesheet, so I can easily do the conversion from any language, e.g. write an Incscape plugin. I’ve improved my stylesheet a bit in the meantime and it works quite well for some SVG files. I don’t aim for complete support of the SVG standard, but make it a useful tool for importing stuff from editors like Inkscape.

The experience was rather painful, since there are many surprises in the SVG files you find in the wild (e.g. some matrix transforms use whitespace, some other use commas as separators, a lot of them define elements that aren’t valid for the parent element, but exist for others, etc.), and there’s a lot of fun doing affine transforms etc. in XSL. I used e(FX)clipse to convert some SVG files to FXML to see what the resulting code should look like. Now it’s time to show the results.

The first SVG image to convert is from Tom Schindl’s original post about his FXML to SVG conversion tool:

Figure 1: “Application-exit” from the Oxygen theme. Left to right: Original SVG file as displayed in Inkscape, FXML produced with my stylesheet as displayed in SceneBuilder, FXML created with e(FX)clipse displayed with SceneBuilder. The small artifacts in the corners of the right image is SceneBuilder background and not part of the FXML.

Tom’s tool was great for optimizing my SVG, since I could use SceneBuilder to compare the two FXML files Node by Node and find what was missing. I think “Tom’s version” still looks a bit better, because the gradient in the gray cross is more like the original, which is caused by a different focusAngle ( actually I think that the one I calculated is correct :-) ).

The next image I used was from an inkscape tutorial about how to draw metal orbs:

Figure 2: From left to right: SVG in Inkscape, my version, e(FX)clipse version. The Blur is due to scaling the image in Seashore.

I used these metal orbs in my pinball, and after manual conversion mine looked exactly like the e(FX)clipse version.  I wasn’t sure why that wan’t correct, so I looked at the code produced by Inkscapes JavaFX 1.x export. The gradients look just the same there. The problem seems to be the gradient radius. My solution was to apply the gradientTransforms also on the radius. I tried it with some other SVGs and it seems to work well. Also in the next picture the gradients look much more like the original than in the e(FX)clipse version (or the Incscape export):

Figure 3: From left to right: SVG in Inkscape, my version, e(FX)clipse version.

In this picture the biggest difference you see is caused by the GaussianBlur in the original (you can see it in the shadows and at the blue shape on the globe). In the beginning I had the stroke-opacity wrong, and the e(FX)clipse version helped me to find that (there are so many ways to define opacity in SVG…). If you look at the original SVG in Safari it looks much more like the e(FX)clipse version, because also Safari also seems to miss the Blur Effect. The gradients on the globe in the right picture also seems to be slightly off, maybe due to a little bug in the calculation of the focusAngle (it seems the focusDistance value is also assigned to focusAngle). Another little difference is that one shape is missing in the right picture, the little star next to the big one. That is caused by the original file using the SVG “use” element (that I didn’t know about before). The next SVG I tried, I found on the web by googling. It’s also from the oxygen theme:

Figure 4: From left to right: SVG in Inkscape, my version, e(FX)clipse version.

This one was a lot of fun. Mine looked exactly like the e(FX)clipse version in the beginning. I don’t actually recall what was wrong here, probably some weird gradient opacity stuff :-) .

So with every new SVG I still find many little issues, and there’s a lot room for improvement. For this I need your feedback. If you’ve got a file you want to import and it produces errors, send it, and I’ll try to fix it in the next version. I prefer files produced with Inkscape or any other popular SVG editor, since it will help creating a plugin that works well with these editors. To try it out use this Stylesheet and use it in NetBeans as shown here.

* Update:

One of the problems with the conversion is, that SVG has Filter-pipelines that cannot be easily modeled in JavaFX. Fortunately the tools I aim to support like Adobe Fireworks and Inkscape use certain recognizable patterns that make it easier to find out what a group of filters actually does without analyzing all the “in” and “out”s.  E.g. Fireworks seems to be so kind to add a comment “Drop Shadow” in a filter defining a Drop Shadow :-) . I updated my stylesheet so it’s now a bit better at handling Fireworks exports, as you can see in this example taken from the “Harmonic Code” Blog:

Figure 5: left to right: Fireworks export, Gerrit's version in JavaFX, my version

As you can see the Drop Shadow offests seem Ok, but it still looks slightly different, because it’s Color is transparent in Gerrit’s version. I’m not sure how I can derive this value from the SVG directly though.

Hidden Treasures of NetBeans: Graphical UI Debugger

NetBeans has a lot of nice features, but some of them are very well hidden. It’s good to have actions only pop up, when they’re actually useful, but sometimes it’s in the way of adoption of a feature. I’ve recently been discussing with some fellow NetBeans users about Scenic View, a tool for exploring live JavaFX Scenes. Most people liked it and asked if we could include it in NetBeans. So I filed an RFE. The RFE was quickly turned down with the explanation that “There is a similar tool in NB already - http://netbeans.org/kb/docs/java/debug-visual.html“. Right, there was this tool included in 7.1, I even wrote about it in an article on DZone.

…and then almost forgot. The reason for this is, that the action only shows up as a toolbar icon during debugging, when your UI is visible (the little camera to the right).

I tried it back then, and it only worked for Swing, maybe because I used a JavaFX preview on the Mac. I tried it again, and it works for JavaFX now as well,  and it allows you to Explore listeners, go to declaration, go to hierarchy Addition. While it doesn’t allow you to watch the live scene, it still allows you to take a snapshot of the application, explore the node hierarchy, and like in Scenic View see all the properties of individual Nodes you select in the snapshot.

The Scenic View does a better job when it comes to debugging the layout, because it has overlays that show baselineOffset, the boundsInParent, and the layoutBounds. But the NetBeans graphical UI Debugger let’s you navigate the code that builds the UI and doesn’t require you to modify your code in order to analyze your Scene. So altogether this tool really is a hidden treasure, that deserves much more publicity. Just add the Layout debugging overlay Feature from Scenic View in the JavaFX support, and it would be even cooler.

And it really deserves a better name than “Graphical UI Debugger“!

“XSL Transformation…” Action in NetBeans

When you’re developing XSLT Stylesheets in NetBeans there’s one neat little feature. If you select an XML-File and a XSL File, and then open the context menu, there’s an Action “XSL Transformation…”. Invoking it will open a dialog that let’s you choose a destination file and specify what should be done after the transformation, either “do nothing”, “default action” (= for plain xml files open in editor), or “open in Browser”. The last one comes very handy if you produce HTML or SVG. Here you can see it in action:

It’s really helpful for developing XSL files. Right now I’m working on a stylesheet to convert SVG to FXML as proposed by Jasper Potts in his blog. Now the cool thing is, that with the SceneBuilder Integration in NetBeans 7.2 the default action for FXML-files is to open up in SceneBuilder:

So it has become extremely easy to test my stylesheet. It’s really cool to see such a simple action combined with the powerful APIs of NetBeans can make your live so much easier.

 

 

JavaFX & SceneBuilder & NetBeans IDE

I’ve just downloaded the latest development release of NetBeans 7.2 to check out new JavaFX features. Here’s one of them: SceneBuilder integration. To edit an FXML file with SceneBuilder simply double-click it or choose “open” from the context menu:

If you want to look at the generated code or do manual edits it in the NetBeans XML-Editor instead, choose “edit” from the context menu. Both tools are ()unlike other IDEs) watching the FileSystem for File updates, so your edits won’t get lost on either side.  And when doing so, you’ll even have some basic code completion for FXML files, which is great, because FXML doesn’t supply a schema or dtd (so it probably was a lot of work to integrate that):

… and we finally have support for the JavaFX  specific CSS tags:

Cool new Features! Looking forward to the release!!!

JavaFX Pinball – where’s the Ball?

Right now the Pinball Machine is running with the debug graphics I created for JBox2D. I created it with a plugin system that allows you to add custom NodeProviders. The only custom rendering you can see  at the moment is the ball. But while experimenting with the Providers for the Bumpers, I found that with the current display they’re too small. You don’t get to see the Gradients, there’s even some ugly Moire effect. So I decided to scale the play field and follow the ball around by simply setting a translate in “the loop” based on the position of the ball. Here’s the result:

Maybe I need to slow down the gameplay a bit… Here’s the same Pinball machine with some custom NodeProviders for the Pinball Elements to give you an idea, what you can do to customize the look. It’s still far from what I want it to look like, though. The performance in the video is quite bad, because the screen recording seems to eat a lot of gnu/cpu cycles. In reality it’s much smoother and the movement of the ball looks quite realistic:

Originally the Top Node used to cover the pinball machine was rendered using SVGPath, but the performance was horrible during translation (following the ball), so I had to replace it with an ImageView.

Update: Using Node.setCache(true); now instead, It basically does the same thing by rendering my node into a buffer and using that image for better performance. So I can still use the Vector graphics and scale my image to whatever resolution I want.

JavaFX MotionBlur

In my little Pinball Machine project, I’m using JBox2D to position the nodes, specifically the ball. In every KeyFrame JBox2D gives me the current position and I simply set the layout of the Node representing the ball. Sometimes the motion looks a bit snatchy. Since I’ve got the current position and the new position I thought I could maybe use that info to create a nice MotionBlur to make the motion look a bit smoother and more realistic. The previous and new position can be used to calculate the angle and radius of the blur. I removed all custom Renderers to only show the rendering of the Ball (the pinball machine actually already looks a lot nicer, more about this later)

Here’s how it looks without (watch in fullscreen in case you want to see the difference :-) ):

And here how it looks with MotionBlur:

I hope the MotionBlur is visible in the Videos… By the way, my “Angry Duke- physics based games with JavaFX” Session has been accepted for W-Jax. Hope to see you there.

Using SceneBuilder for designing JavaFX Pinball Bumpers

I begin to like SceneBuilder. It sure still has limitations, e.g. you cannot add Custom Nodes, you cannot edit Shapes, etc. but it’s still useful. And after all I heard, it will probably be Open Source, so maybe I can contribute and add some small bits. I’m experimenting a bit with SceneBuilder now, to create designs for my JBox2D & JavaFX Pinball machine.

Now that I’m able to design my Gradients with my GradientEditor (another function that I’d like to see in SceneBuilder :-) ), I’ve got everything I need to design Nodes. Here’s my first shot at the Pinball Bumpers:

The best approach to working with SceneBuilder is to begin with the design, and when you hit a situation you can’t handle in there, switch to NetBeans and edit the FXML directly. I did that, e.g. for modifying the Shape from the default Triangle to a Star. I think the bumpers look OK for a first try – looking forward to put them to action :-) .

Update: I added a background, highlights and drop shadows and the pinball itself to the design. The flippers will probably be more complicated. I started using Inkscape to create the Shapes, and import them using SVGPath (not displayed here), but I’m not sure how to do the gradients for creating a nice 3D effect…