December 8, 2024

How to Convert a p5.js Game into Java (JavaFX)

If you’ve previously built an interactive game with p5.js and now want to port it to a Java-based desktop application, you might be wondering where to start. This article walks through the process of converting a complex p5.js planetary fleet battle simulation into a JavaFX version. We’ll discuss key differences between p5.js and JavaFX, show how to replace p5.js global functions with JavaFX application logic, and highlight various code translation techniques.

Prerequisites

Before we begin, ensure the following setup:

Framework Conversion: p5.js vs. JavaFX

p5.js Approach:
In p5.js, you rely on global functions like setup(), draw(), mousePressed(), keyPressed(), etc. The environment calls these automatically. You also get a global createCanvas(...) function that sets up the drawing surface, and draw() is called repeatedly at a given frame rate.

JavaFX Approach:
In JavaFX, we don’t have a global drawing loop by default. Instead:

For example, where p5.js uses createCanvas(1200, 900), JavaFX code might look like:

Canvas canvas = new Canvas(1200, 900);
GraphicsContext gc = canvas.getGraphicsContext2D();

Then, to simulate draw(), we set up:

new AnimationTimer() {
@Override
public void handle(long now) {
// The equivalent of draw()
// Update game state and call drawing methods here
}
}.start();

Mouse and keyboard events are added to the Scene:

scene.setOnMousePressed(e -> {
// Handle mouse click, equivalent to mousePressed()
});
scene.setOnKeyPressed(e -> {
// Handle keyboard input, equivalent to keyPressed()
});
scene.setOnScroll(e -> {
// Handle mouse wheel, equivalent to mouseWheel()
});

Translating Drawing Functions

p5.js provides drawing commands like ellipse(x, y, w, h) or text(txt, x, y) which are drawn with the center-based coordinate system. JavaFX, however, often uses top-left coordinates. Also, p5.js fill() and stroke() carry over drawing styles, while JavaFX GraphicsContext methods explicitly set fill and stroke before drawing.

For complex shapes where p5.js uses beginShape(), vertex(), endShape(), you can store your points in an array and use gc.strokePolyline(...) or gc.fillPolygon(...) in JavaFX.

Data Structures and Architecture

The original p5.js code may have relied heavily on global variables and arrays. In Java, it’s cleaner and more maintainable to encapsulate data into classes and use ArrayList instead of raw arrays. This also helps when converting modal dialogs, buttons, and other components into nested classes.

For instance, where you previously wrote:

let ships = [];

you now have:

ArrayList<Ship> ships = new ArrayList<>();

and Ship becomes a dedicated class with attributes and methods.

We also might encapsulate GUI elements like Button or Modal into inner classes for cleanliness and organization.

Handling Events

p5.js global event functions like mousePressed() or keyPressed() are straightforward. In JavaFX, you set event listeners on the Scene or Canvas:

For continuous key states (similar to keyIsDown() in p5.js), maintain a boolean array or a Set<KeyCode> in keyPressed and keyReleased events.

Mathematical Differences

For map() function, which p5.js uses to re-map a value from one range to another, implement a custom method:

double mapValue(double value, double start1, double stop1, double start2, double stop2) {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
}

Alerts and Cursor Changes

If you previously used window.location.reload() in p5.js, you don’t have such a function in JavaFX. A suitable replacement might be System.exit(0) to terminate the application, or reinitialize your states to simulate a “restart.”

Final Thoughts

The key idea is to replace each p5.js global function and variable with a structured JavaFX approach. The draw() loop maps naturally to an AnimationTimer, setup() logic goes into start() or an initialization method, and event handling transitions from global functions to scene event listeners.

Where p5.js draws instantly and automatically, JavaFX requires you to call drawing methods on a GraphicsContext at each frame. State is maintained as class fields rather than global variables. Complex shapes and transitions require more manual math and array handling, but the result is a robust, object-oriented Java application.

You can check the final Java code provided above as an example. It takes the original p5.js game (which included planet rotation, fleet formation, equipment selection, and red point enemy attacks) and ports it into a JavaFX environment. Every function and global variable from p5.js now lives in a structured Java class, event handlers replicate the behavior of mousePressed() and keyPressed(), and drawing routines carefully translate ellipses, rectangles, lines, and text.

This approach gives you a powerful and flexible desktop version of your original web-based p5.js game, leveraging the strengths of Java’s strong typing, richer libraries, and performance.

About this Post

This post is written by FFFeiya, licensed under CC BY-NC 4.0.

#p5.js#JavaFX