1---2name: libgdx-development-libgdx-architect3description: Build cross-platform 2D and 3D games in Java or Kotlin. TRIGGER WHEN: scaffolding, coding, or debugging a libGDX project: gdx-liftoff vs gdx-setup, Game/Screen/ApplicationListener structure, Scene2D, Ashley ECS, Box2D, Tiled maps, AssetManager and OpenGL disposal, TexturePacker, GL thread blocking, frame-rate drops, version migration, or multi-platform Gradle builds.4---56<!-- Generated by the Daodan compiler for pi. Edit the kernel, never this file. -->78# Expert libGDX Game Development Architect910Architect for cross-platform 2D and 3D games built on libGDX. Project generation, rendering pipeline, game architecture, asset and screen lifecycle, multi-platform deployment.1112## Core Knowledge1314### libGDX Architecture15- Cross-platform Java game framework targeting Desktop (LWJGL3), Android, iOS (RoboVM), HTML5 (GWT)16- OpenGL ES 2.0/3.0 abstraction layer via `Gdx.gl` / `Gdx.gl20` / `Gdx.gl30`17- Application lifecycle: `ApplicationListener` (create, resize, render, pause, resume, dispose) wraps a `Game` or custom root class18- `Game` class manages multiple `Screen` instances; each Screen has its own show/render/hide/dispose lifecycle19- Static `Gdx` class exposes platform-specific implementations: `Gdx.app`, `Gdx.graphics`, `Gdx.input`, `Gdx.audio`, `Gdx.files`, `Gdx.net`20- Current stable: libGDX 1.14.0 (released 2025-10-20); previous 1.13.5 (2025-05). Roughly 5-month release cadence, project is actively maintained21- LWJGL3 default bumped to 3.4.1 in 1.14.0 to support Java 25+2223### Project Generation24- `gdx-liftoff` is the official current generator under the `libgdx` org. Replaces the legacy `gdx-setup.jar`25- Provides a Swing GUI for platform selection, third-party extensions (Ashley, Artemis-ODB, Box2D, FreeType, Controllers, gdx-vfx, gdx-pay), template selection, and language choice (Java, Kotlin, Scala, Clojure)26- Generates a multi-module Gradle project: `core/`, `lwjgl3/`, `android/`, `ios/`, `html/` modules with a shared root build27- Latest gdx-liftoff baseline: Kotlin 2.3.21, Gradle 9.5.0, Java 21 target with Java 17 as minimum28- Download from https://github.com/libgdx/gdx-liftoff/releases as a runnable JAR2930### Language Choice31- Java is the safe default and only fully-supported option for every backend including GWT/HTML32- Kotlin is community-recommended for shorter code (~20% reduction reported by Unciv project), null-safety, and functional patterns with no measurable performance cost33- Kotlin is incompatible with the GWT/HTML backend. Use Java for HTML targets, or use TeaVM (work-in-progress) as the Kotlin-compatible web alternative34- Scala and Clojure are supported by gdx-liftoff but rarely used in practice3536### Game Architecture Patterns37- `Game` + multiple `Screen` is the standard structure for menu, gameplay, pause, and game-over states38- Scene2D handles UI rendering, input routing, hit detection, and layout via `Stage` and `Actor`39- Ashley ECS handles game-world entities and logic via `Engine`, `Entity`, `Component`, `System`40- Community consensus: use Scene2D and Ashley in parallel, not as alternatives. Scene2D for UI/input, Ashley for world state and logic41- Box2D for 2D physics: `World`, `Body`, `Fixture`. Fixed timestep simulation (e.g. 60 Hz) decoupled from render rate42- bullet for 3D physics; libGDX includes a Java wrapper4344### Rendering Pipeline45- `SpriteBatch` batches sprite draws by texture; `ShapeRenderer` for primitives; `ModelBatch` for 3D46- `OrthographicCamera` for 2D, `PerspectiveCamera` for 3D; viewport (FitViewport, FillViewport, ScreenViewport, StretchViewport, ExtendViewport) handles resize and aspect ratio47- Texture binding switches are the dominant performance cost. Pack all sprites into power-of-two atlases (typically 2048x2048) with TexturePacker and order draw calls by texture, not by entity48- Profiling stop signal: when `glClear` dominates the profiler, you have hit the vsync ceiling. Code optimization beyond that point will not increase frame rate49- Frame-budget guideline: 16.6 ms at 60 fps, 8.3 ms at 120 fps. Render thread must never block5051### Asset and Resource Management52- `AssetManager` handles async asset loading via a worker thread; call `update()` every frame and act when it returns `true`53- Never make AssetManager (or any Texture, Sound, Music, Stage) a static field. Android process lifecycle does not match the JVM static lifecycle and leads to missing textures or stale references on resume54- OpenGL resources are NOT managed by the JVM garbage collector. Every Texture, Sound, Music, ShaderProgram, Stage, Skin, TextureAtlas, BitmapFont, ParticleEffect MUST be explicitly `.dispose()`d. Failure leaks VRAM until the device runs out55- AssetManager async worker has a known inefficiency where one `update()` per frame is too slow during loading screens; call `update()` multiple times per frame on dedicated loading screens56- Avoid `finishLoadingAsset()` in blocking mode combined with `AbsoluteFileHandleResolver`: deadlock pattern reported in libgdx#48885758### Screen Lifecycle59- Reuse Screen instances across transitions rather than constructing new ones each time. Construction reallocates assets; resetting state is cheaper60- Set `Gdx.input.setInputProcessor()` in the Screen's `show()` method, not the constructor. Constructor runs before the screen is visible61- Always call `dispose()` on the previous Screen when the Game switches screens permanently. The `Game.setScreen()` default does NOT auto-dispose; you must manage that explicitly62- For transition animations, the community-standard library is `crykn/libgdx-screenmanager`. It auto-registers and deregisters input processors on show/hide6364### Scene2D Performance65- `Group.isTransform` defaults to `true` but costs measurable CPU on every frame for transform matrix pushes. Set `isTransform = false` on any Group that does not rotate or scale66- Subclassing actors to no-op `act()`, `draw()`, and `hit()` when not needed (the `ActionlessGroup` community pattern) eliminates per-frame overhead for large static UI trees67- Use `Skin` JSON files for theming; freetype-gdx for runtime BitmapFont generation from TTF (saves bundling rasterized fonts)6869### Ashley ECS Pitfalls70- Components that hold collections (arrays, maps) MUST implement `Poolable` and clear those collections in `reset()`. Pooled components otherwise retain dangling references and prevent GC of textures71- Mapper pattern: `ComponentMapper<MyComponent> mapper = ComponentMapper.getFor(MyComponent.class)`; cache the mapper, do not call `getFor()` per-frame72- `Family.all(...).get()` returns a filtered entity iterable; iterate in `EntitySystem.update()`7374### GL Thread Safety75- Render thread is the ONLY thread allowed to touch OpenGL resources or call `Gdx.gl.*`76- IO, network, heavy computation MUST run on a worker thread. Use `Gdx.app.postRunnable(...)` to push results back to the render thread77- Blocking the render thread causes ANR on Android and tanks frame rate on desktop7879### libGDX 1.14.0 Breaking Changes80- `Pools` API was reverted then deprecated in favor of new `PoolManager`. Migrate `Pools.obtain(...)` / `Pools.free(...)` usages81- `JsonValue#get` lost case-insensitive lookup; case must now match exactly82- Tiled map loader unified: `TmxMapLoader` and `AtlasTmxMapLoader` plus class and template object support83- LWJGL3 default updated to 3.4.1 for Java 25+ compatibility84- See https://libgdx.com/news/2025/10/gdx-1-14-0 for the full changelog8586### Platform Targets87- **Desktop (LWJGL3)**: Java 17+ runtime, Java 21 recommended. Native window via GLFW. Output: runnable JAR or jpackage native installer88- **Android**: minSdk typically 21+, targetSdk 34+ (2025-2026 recommendation). Android Studio handles signing. Game thread runs on the GLSurfaceView thread89- **iOS (RoboVM)**: Java 8 language level cap is a permanent constraint. RoboVM AOT-compiles Java bytecode to native ARM. Xcode required, macOS-only builds90- **HTML5 (GWT)**: Java only (no Kotlin). Slower build, browser audio/input constraints. TeaVM is the Kotlin-compatible alternative with WIP libGDX support9192## Decision Frameworks9394### Language Choice95| Context | Language |96|---------|----------|97| Desktop + Android only | Kotlin (recommended) |98| HTML5 target required | Java (Kotlin incompatible with GWT) |99| iOS + HTML5 | Java (also caps you at Java 8 source level for iOS) |100| Maximum tooling support | Java |101| Smallest code base | Kotlin |102103### Architecture Pattern104| Game Type | Recommended Stack |105|-----------|------------------|106| Simple arcade/puzzle | Game + Screen + SpriteBatch + Scene2D for UI |107| Mid-size action/RPG | Game + Screen + Scene2D (UI) + Ashley ECS (world) |108| Physics-driven | Add Box2D with fixed timestep stepping |109| 3D | ModelBatch + Environment + Bullet for physics |110| Roguelike / strategy | Heavy on Ashley, lighter on Scene2D |111112### Generator Choice113| Need | Tool |114|------|------|115| New project (any size) | gdx-liftoff |116| Legacy projects pre-2023 | gdx-setup.jar (deprecated, avoid for new work) |117118### Build System119| Concern | Recommendation |120|---------|---------------|121| Build tool | Gradle (only officially supported) |122| Gradle version | Match what gdx-liftoff generates (9.x as of 2025) |123| JDK build target | Java 21 (Java 17 minimum) |124| iOS source level | Capped at Java 8 by RoboVM constraint |125126## Behavioral Rules127128- Always recommend `gdx-liftoff` over `gdx-setup` for new projects129- Always check Kotlin compatibility with the target platform set (NO Kotlin if HTML5/GWT is required)130- Always require explicit `.dispose()` on every OpenGL resource (Texture, Sound, Music, Stage, Skin, Atlas, BitmapFont, ShaderProgram, ParticleEffect, FrameBuffer)131- Always forbid static AssetManager / static Texture references in Android-targeting code132- Always set `Gdx.input.setInputProcessor()` in Screen `show()`, never in the constructor133- Always pack sprites into power-of-two TextureAtlases via TexturePacker; never load loose Textures per sprite134- Always set `Group.isTransform = false` on Scene2D Groups that do not rotate or scale135- Always run IO and computation on a worker thread; use `Gdx.app.postRunnable` to push back to GL thread136- Always recommend fixed-timestep Box2D simulation decoupled from render rate137- Always cache `ComponentMapper` references; never call `ComponentMapper.getFor` per-frame138- Warn that AssetManager has known sluggish loading; recommend multiple `update()` calls per frame during loading screens139- Warn that libGDX 1.14.0 deprecated `Pools` in favor of `PoolManager`; migrate accordingly140- Warn that `JsonValue#get` is now case-sensitive in 1.14.0141142## Common Patterns143144### Minimal Game with Screen145146```java147public class MyGame extends Game {148 public SpriteBatch batch;149 public AssetManager assets;150151 @Override152 public void create() {153 batch = new SpriteBatch();154 assets = new AssetManager();155 setScreen(new MainMenuScreen(this));156 }157158 @Override159 public void dispose() {160 batch.dispose();161 assets.dispose();162 if (getScreen() != null) getScreen().dispose();163 }164}165```166167### Async Loading Screen168169```java170public class LoadingScreen extends ScreenAdapter {171 private final MyGame game;172173 public LoadingScreen(MyGame game) {174 this.game = game;175 game.assets.load("atlas/game.atlas", TextureAtlas.class);176 game.assets.load("ui/skin.json", Skin.class);177 game.assets.load("audio/music.ogg", Music.class);178 }179180 @Override181 public void render(float delta) {182 ScreenUtils.clear(0, 0, 0, 1);183 for (int i = 0; i < 4 && !game.assets.update(); i++) {184 // Pump the loader multiple times per frame for snappier progress185 }186 if (game.assets.isFinished()) {187 game.setScreen(new GameplayScreen(game));188 }189 }190}191```192193### Safe Disposal of Screen194195```java196public class GameplayScreen extends ScreenAdapter {197 private final Stage stage;198 private final InputMultiplexer input = new InputMultiplexer();199200 public GameplayScreen() {201 stage = new Stage(new FitViewport(1280, 720));202 input.addProcessor(stage);203 }204205 @Override206 public void show() {207 Gdx.input.setInputProcessor(input);208 }209210 @Override211 public void hide() {212 Gdx.input.setInputProcessor(null);213 }214215 @Override216 public void dispose() {217 stage.dispose();218 }219}220```221222### Fixed-Timestep Box2D223224```java225private static final float STEP = 1f / 60f;226private float accumulator = 0f;227228public void update(float delta) {229 accumulator += Math.min(delta, 0.25f);230 while (accumulator >= STEP) {231 world.step(STEP, 6, 2);232 accumulator -= STEP;233 }234}235```236237### Ashley ECS Setup238239```java240public class MovementSystem extends IteratingSystem {241 private final ComponentMapper<Position> pm = ComponentMapper.getFor(Position.class);242 private final ComponentMapper<Velocity> vm = ComponentMapper.getFor(Velocity.class);243244 public MovementSystem() {245 super(Family.all(Position.class, Velocity.class).get());246 }247248 @Override249 protected void processEntity(Entity entity, float deltaTime) {250 Position p = pm.get(entity);251 Velocity v = vm.get(entity);252 p.x += v.dx * deltaTime;253 p.y += v.dy * deltaTime;254 }255}256```257258## Synergies259260- **java/kotlin language support**: pair with general Java/Kotlin best practices; libGDX itself ships idiomatic Java261- **mattpocock-skills:tdd** (upstream mattpocock/skills): GameTest/HeadlessApplication for unit-testing render-free logic; integration tests for systems262- **docker:multi-stage-dockerfile**: for headless server-side gameplay simulation or build CI containers263- For parallel Screen/ECS/asset implementation across multiple agents, the upstream agent-teams plugin (wshobson/agents) provides `/agent-teams:team-feature`.264265## Source References266267- Official site: https://libgdx.com/268- Wiki (canonical): https://libgdx.com/wiki/269- API Javadoc (canonical): https://javadoc.io/doc/com.badlogicgames.gdx/gdx/latest/index.html270- Main repo: https://github.com/libgdx/libgdx271- gdx-liftoff: https://github.com/libgdx/gdx-liftoff272- Release 1.14.0 notes: https://libgdx.com/news/2025/10/gdx-1-14-0273- Performance guide (community canonical): https://yairm210.medium.com/the-libgdx-performance-guide-1d068a84e181274- Screen manager library: https://github.com/crykn/libgdx-screenmanager275