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:
- JDK Installed: Make sure you have a JDK installed and properly configured.
- JavaFX SDK Installed: Download and integrate the JavaFX SDK. You’ll need to add JavaFX libraries (like
javafx.controls
,javafx.graphics
) to your project’s classpath. - IntelliJ IDEA Community or other IDE: We’ll use IntelliJ IDEA Community as our reference environment. Configure your project to include the JavaFX SDK and set VM arguments such as:
--module-path "path/to/javafx-sdk/lib" --add-modules=javafx.controls,javafx.fxml
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:
- We start from the
Application
class’sstart()
method to create aCanvas
andGraphicsContext
. - We use an
AnimationTimer
to implement the equivalent ofdraw()
. The timer’shandle(long now)
method is called once per frame, allowing you to update and redraw your scene. - Event handlers on the
Scene
manage keyboard, mouse, and scroll events—replacingmousePressed()
,keyPressed()
, andmouseWheel()
from p5.js.
For example, where p5.js uses createCanvas(1200, 900)
, JavaFX code might look like:
|
Then, to simulate draw()
, we set up:
|
Mouse and keyboard events are added to the Scene
:
|
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.
ellipse(x, y, w, h) in p5.js is centered at
(x, y)
. In JavaFX:gc.fillOval(x - w/2, y - h/2, w, h);
rect(x, y, w, h) in p5.js is also often center-based if you’ve set
rectMode(CENTER)
, but by default it’s top-left. JavaFX similarly draws from top-left:gc.fillRect(x, y, w, h);
line(x1, y1, x2, y2) in JavaFX:
gc.strokeLine(x1, y1, x2, y2);
text(txt, x, y): In p5.js,
text()
is centered or aligned based ontextAlign()
. In JavaFX,fillText()
defaults to top-left. Adjust your coordinates or usegc.setTextAlign(TextAlignment.CENTER)
:gc.fillText(txt, x, y);
push()/pop() in p5.js helps save and restore drawing states (like transformations). JavaFX doesn’t have a direct equivalent. For complex transformations, you might manage your own transformations or simply recalculate coordinates without relying on stack-based transformations.
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:
|
you now have:
|
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
:
mousePressed() equivalent:
scene.setOnMousePressed(e -> {
double mx = e.getX();
double my = e.getY();
// handle mouse pressed logic
});keyPressed() equivalent:
scene.setOnKeyPressed(e -> {
KeyCode code = e.getCode();
// handle key pressed logic
});mouseWheel() equivalent:
scene.setOnScroll(e -> {
double deltaY = e.getDeltaY();
// handle mouse wheel logic
});
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
- random(1) in p5.js can be replaced by
Math.random()
in Java, which returns a double between 0 and 1. - radians() can be replaced by
Math.toRadians()
. - p5.js constants like
TWO_PI
can be defined asdouble TWO_PI = 2 * Math.PI;
.
For map()
function, which p5.js uses to re-map a value from one range to another, implement a custom method:
|
Alerts and Cursor Changes
- alert() in p5.js might just show a browser popup. In JavaFX, you can either use
System.out.println()
or create a simpleAlert
dialog. - p5.js
cursor(HAND)
can be translated toscene.setCursor(Cursor.HAND)
in JavaFX.
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.