Demo #2 Star Field
This is the second example in a weekly series of demos of using JavaFX Canvas and Nashorn.
(I suspect you did not arrive here from a link in “JavaFX Links of the Week”!).
For this demo, I chose a classic “star field” animation where a field of stars moves directly at the camera, using the correct perspective, velocity and dimensions. I even added a nice galaxy image in the background that fades in and out.
Again, the Nashorn script is amazingly simple.
Here is a short video of the demo in action:
As you can see, the frame rate (FPS) is, once again, fairly impressive and I can assure you this runs father smoother than in any browser I tried with their “native” html5 Canvas/javascript implementation.
The full source for this demo (including the Nashorn script, the images and sample BAT file) can be downloaded using this link: Star Field Demo (ZIP)
The Nashorn code is shown here for convenience:
/* * This code is covered by the Bembrick Open World License (BOWL). * * This means this code is completely free of any restrictions on usage. * * Feel free to study it, modify it, redistribute it and even claim it as * your own if you like! * * Courtesy of Bembrick Software Labs in the interest of promoting JavaFX. */ var Random = Java.type("java.util.Random"); var File = Java.type("java.io.File"); var Image = Java.type("javafx.scene.image.Image"); var Color = Java.type("javafx.scene.paint.Color"); var Canvas = Java.type("javafx.scene.canvas.Canvas"); var Border = Java.type("javafx.scene.layout.Border"); var BorderStroke = Java.type("javafx.scene.layout.BorderStroke"); var BorderStrokeStyle = Java.type("javafx.scene.layout.BorderStrokeStyle"); var BorderWidths = Java.type("javafx.scene.layout.BorderWidths"); var BorderPane = Java.type("javafx.scene.layout.BorderPane"); var CornerRadii = Java.type("javafx.scene.layout.CornerRadii"); var StackPane = Java.type("javafx.scene.layout.StackPane"); var Scene = Java.type("javafx.scene.Scene"); var Font = Java.type("javafx.scene.text.Font"); var FontSmoothingType = Java.type("javafx.scene.text.FontSmoothingType"); var Text = Java.type("javafx.scene.text.Text"); var AnimationTimer = Java.type("javafx.animation.AnimationTimer"); var PerformanceTracker = Java.type("com.sun.javafx.perf.PerformanceTracker"); var AnimationTimerExtend = Java.extend(AnimationTimer); var TITLE_STAGE = "Star Field by Bembrick Software Labs (JavaFX Canvas/Nashorn)"; var WIDTH = 1280; var HEIGHT = 720; var STAR_COUNT = 1000; var radius = 1; var centerX; var centerY; var stars = []; var star; var i; var CANVAS = new Canvas(WIDTH, HEIGHT); var BORDER_CANVAS = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(4))); var focalLength = WIDTH; var RAND = new Random(42); var FONT = new Font("Sans Serif", 24); var LABEL_FPS = new Text(); var IMAGE_BACKGROUND = new Image(fileToURL("background.jpg")); var lastTime = 0; var lastFpsUpdate = { time: 0, value: 0 }; var fps = 60; var alpha = 0.0; var delta = 0.0003; initializeStars(); function randomLightRGB() { return RAND.nextInt(255 - 175) + 175; } function randomLightColor() { return Color.rgb(randomLightRGB(), randomLightRGB(), randomLightRGB()); } function initializeStars(){ centerX = CANVAS.width / 2; centerY = CANVAS.height / 2; stars = []; for (i = 0; i < STAR_COUNT; i++) { star = { x: Math.random() * CANVAS.width, y: Math.random() * CANVAS.height, z: Math.random() * CANVAS.width, c: randomLightColor() }; stars.push(star); } } function moveStars(){ for (i = 0; i < STAR_COUNT; i++) { star = stars[i]; star.z--; if (star.z <= 0) { star.z = CANVAS.width; } } } function drawStars() { var pixelX, pixelY, pixelRadius; var c = CANVAS.graphicsContext2D; c.setFill(Color.BLACK); c.fillRect(0,0, CANVAS.width, CANVAS.height); c.setGlobalAlpha(alpha); if (alpha > 0.8) { delta = -0.0003; } else if (alpha < 0.2) { delta = 0.0003; } alpha += delta; c.drawImage(IMAGE_BACKGROUND, 0, 0, CANVAS.width, CANVAS.height); c.setGlobalAlpha(1.0); for (i = 0; i < STAR_COUNT; i++) { star = stars[i]; c.setFill(star.c); pixelX = (star.x - centerX) * (focalLength / star.z); pixelX += centerX; pixelY = (star.y - centerY) * (focalLength / star.z); pixelY += centerY; pixelRadius = radius * (focalLength / star.z) * 1.5; c.fillOval(pixelX, pixelY, pixelRadius, pixelRadius); } } function renderFrame() { moveStars(); drawStars(); } function calculateFPS(now) { var diff = now - lastTime; var fps = 1000000000 / diff; if (diff > 1500000) { LABEL_FPS.setText("Frame Rate: " + Math.floor(tracker.getAverageFPS() + 0.5) + " FPS"); } lastTime = now; return fps; } function animate(now) { fps = calculateFPS(now); renderFrame(); } var timer = new AnimationTimerExtend() { handle: function handle(now) { animate(now); } }; function fileToURL(file) { return new File(file).toURI().toURL().toExternalForm(); } var stack = new StackPane(); var pane = new BorderPane(); pane.setCenter(CANVAS); pane.setBorder(BORDER_CANVAS); LABEL_FPS.setManaged(false); LABEL_FPS.setFont(FONT); LABEL_FPS.setFontSmoothingType(FontSmoothingType.LCD); LABEL_FPS.setFill(Color.AQUAMARINE); LABEL_FPS.setX(20); LABEL_FPS.setY(42); stack.getChildren().add(pane); stack.getChildren().add(LABEL_FPS); $STAGE.scene = new Scene(stack); $STAGE.title = TITLE_STAGE; $STAGE.resizable = false; var tracker = PerformanceTracker.getSceneTracker($STAGE.scene); timer.start();The command line to use to run the Nashorn script is:
jjs -fx -Djavafx.animation.fullspeed=true -scripting star-field.js(but you may have to either fully specify the path to the jjs command which is located in your JRE bin folder or add it to your system path, use an alias etc.)
I hope to be able to post a new example every week and please use the Comments section below if you have any questions or comments to make in general.
Just my 2 bits,
Felix