From 0f6bbb5bc50d6ebf452b6a6bd3d82fd2649ff1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=E1=BA=BF=20H=C6=B0ng?= Date: Mon, 29 Sep 2025 20:42:10 +0700 Subject: [PATCH] chore(engine): switch to libGDX For real --- app/build.gradle.kts | 30 +++++++++++++++++- .../java/org/vibecoders/moongazer/App.java | 25 --------------- .../org/vibecoders/moongazer/Constants.java | 15 +++++++++ .../java/org/vibecoders/moongazer/Game.java | 29 +++++++++++++++++ .../java/org/vibecoders/moongazer/Main.java | 23 ++++++++++++++ app/src/main/resources/icons/logo.png | Bin 0 -> 17819 bytes .../moongazer/{AppTest.java => GameTest.java} | 0 gradle.properties | 2 +- 8 files changed, 97 insertions(+), 27 deletions(-) delete mode 100644 app/src/main/java/org/vibecoders/moongazer/App.java create mode 100644 app/src/main/java/org/vibecoders/moongazer/Constants.java create mode 100644 app/src/main/java/org/vibecoders/moongazer/Game.java create mode 100644 app/src/main/java/org/vibecoders/moongazer/Main.java create mode 100644 app/src/main/resources/icons/logo.png rename app/src/test/java/org/vibecoders/moongazer/{AppTest.java => GameTest.java} (100%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ba339e6..a157698 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,8 @@ * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.0.0/userguide/building_java_projects.html in the Gradle documentation. */ +val libgdxVersion = "1.13.5" + plugins { // Apply the application plugin to add support for building a CLI application in Java. application @@ -29,6 +31,27 @@ dependencies { // This dependency is used by the application. implementation(libs.guava) + + // ----- LibGDX Core / Extras ----- + implementation("com.badlogicgames.gdx:gdx:$libgdxVersion") + implementation("com.badlogicgames.gdx:gdx-box2d:$libgdxVersion") + implementation("com.badlogicgames.gdx:gdx-freetype:$libgdxVersion") + implementation("com.badlogicgames.ashley:ashley:1.7.4") + + // ----- LibGDX Desktop (LWJGL3 + natives) ----- + implementation("com.badlogicgames.gdx:gdx-backend-lwjgl3:$libgdxVersion") + implementation("com.badlogicgames.gdx:gdx-platform:$libgdxVersion:natives-desktop") + implementation("com.badlogicgames.gdx:gdx-box2d-platform:$libgdxVersion:natives-desktop") + implementation("com.badlogicgames.gdx:gdx-freetype-platform:$libgdxVersion:natives-desktop") + + // Tools (exclude legacy LWJGL backend) + implementation("com.badlogicgames.gdx:gdx-tools:$libgdxVersion") { + exclude(group = "com.badlogicgames.gdx", module = "gdx-backend-lwjgl") + } + + // Logging + implementation("org.slf4j:slf4j-api:2.1.0-alpha1") + implementation("ch.qos.logback:logback-classic:1.5.18") } // Apply a specific Java toolchain to ease working on different environments. @@ -40,7 +63,12 @@ java { application { // Define the main class for the application. - mainClass = "org.vibecoders.moongazer.App" + mainClass = "org.vibecoders.moongazer.Main" +} + +tasks.withType().configureEach { + options.encoding = "UTF-8" + options.release.set(23) } tasks.named("test") { diff --git a/app/src/main/java/org/vibecoders/moongazer/App.java b/app/src/main/java/org/vibecoders/moongazer/App.java deleted file mode 100644 index f1087ef..0000000 --- a/app/src/main/java/org/vibecoders/moongazer/App.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.vibecoders.moongazer; - -import javafx.application.Application; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - -public class App extends Application { - - @Override - public void start(Stage stage) { - String javaVersion = System.getProperty("java.version"); - String javafxVersion = System.getProperty("javafx.version"); - Label l = new Label("Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "."); - Scene scene = new Scene(new StackPane(l), 640, 480); - stage.setScene(scene); - stage.show(); - } - - public static void main(String[] args) { - launch(); - } - -} diff --git a/app/src/main/java/org/vibecoders/moongazer/Constants.java b/app/src/main/java/org/vibecoders/moongazer/Constants.java new file mode 100644 index 0000000..7e6351c --- /dev/null +++ b/app/src/main/java/org/vibecoders/moongazer/Constants.java @@ -0,0 +1,15 @@ +package org.vibecoders.moongazer; + +/** + * Client configuration constants and default values + * used throughout the Moongazer client application. + */ +public class Constants { + + /** + * Window configuration. + */ + public static final int WINDOW_WIDTH = 1280; + public static final int WINDOW_HEIGHT = 720; + public static final String WINDOW_TITLE = "Moongazer"; +} diff --git a/app/src/main/java/org/vibecoders/moongazer/Game.java b/app/src/main/java/org/vibecoders/moongazer/Game.java new file mode 100644 index 0000000..df9cba7 --- /dev/null +++ b/app/src/main/java/org/vibecoders/moongazer/Game.java @@ -0,0 +1,29 @@ +package org.vibecoders.moongazer; + +import com.badlogic.gdx.ApplicationAdapter; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL20; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.vibecoders.moongazer.Constants.*; + +public class Game extends ApplicationAdapter { + private static final Logger log = LoggerFactory.getLogger(Game.class); + + @Override + public void create() { + log.debug("create stub"); + } + + @Override + public void render() { + Gdx.gl.glClearColor(0, 0, 0, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + } + + @Override + public void dispose() { + log.debug("stub"); + } +} + diff --git a/app/src/main/java/org/vibecoders/moongazer/Main.java b/app/src/main/java/org/vibecoders/moongazer/Main.java new file mode 100644 index 0000000..0669654 --- /dev/null +++ b/app/src/main/java/org/vibecoders/moongazer/Main.java @@ -0,0 +1,23 @@ +package org.vibecoders.moongazer; + +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.vibecoders.moongazer.Constants.*; + +public class Main { + + private static final Logger log = LoggerFactory.getLogger(Main.class); + + public static void main(String[] args) { + Lwjgl3ApplicationConfiguration cfg = new Lwjgl3ApplicationConfiguration(); + cfg.setTitle(WINDOW_TITLE); + cfg.setWindowedMode(WINDOW_WIDTH, WINDOW_HEIGHT); + cfg.useVsync(true); + cfg.setIdleFPS(10); + cfg.setWindowIcon("icons/logo.png"); + log.info("Starting game client"); + new Lwjgl3Application(new Game(), cfg); + } +} diff --git a/app/src/main/resources/icons/logo.png b/app/src/main/resources/icons/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0e68307d66deddf33a097203eec59d50bd34a051 GIT binary patch literal 17819 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