A 2D top-down game built with libGDX - an educational project demonstrating game development fundamentals.
- Java 19
- libGDX 1.14.0 - Cross-platform game framework
- Gradle - Build automation
- JDK 19 or higher
- Git
./gradlew lwjgl3:run./gradlew lwjgl3:jar
# Runnable JAR will be at: lwjgl3/build/libs/This project was built incrementally. Follow these steps to understand the architecture or recreate it from scratch.
Use gdx-liftoff to generate the base project:
# Download gdx-liftoff from GitHub releases
# Run the generator and select:
# - libGDX version: 1.14.0
# - Platforms: core, lwjgl3 (desktop)
# - Java version: 19Project structure:
libgdx-game/
├── core/ # Shared game logic (main module)
├── lwjgl3/ # Desktop launcher
├── assets/ # Game resources (images, sounds, fonts)
├── build.gradle # Root build configuration
└── settings.gradle
File: core/src/main/java/io/github/lostfly/GameApp.java
The GameApp class extends libGDX's Game - the main application class that manages screens.
Key concepts:
SpriteBatch- used for rendering 2D sprites, created once and sharedsetScreen()- switches between MenuScreen and GameScreen
public class GameApp extends Game {
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
public SpriteBatch batch;
@Override
public void create() {
batch = new SpriteBatch();
setScreen(new MenuScreen(this));
}
}File: core/src/main/java/io/github/lostfly/screen/MenuScreen.java
Main menu with title and start prompt.
Key concepts:
ScreenAdapter- abstract class so you don't need to implement all Screen methodsBitmapFont- built-in font for text renderingGlyphLayout- for text measurement and centering
Controls:
ENTER- Start gameESC- Exit
File: core/src/main/java/io/github/lostfly/screen/GameScreen.java
The main gameplay screen where all game logic happens.
Key concepts:
OrthographicCamera- 2D camera that follows the playerFitViewport- maintains aspect ratio across different screen sizesShapeRenderer- draws shapes (rectangles, circles) for debugging/prototyping
Camera setup:
camera = new OrthographicCamera();
viewport = new FitViewport(GameApp.WIDTH, GameApp.HEIGHT, camera);Game loop (render method):
- Clear screen
- Process input (keyboard + joystick)
- Update game objects
- Update camera position
- Render world (with camera matrix)
- Render HUD (without camera matrix)
File: core/src/main/java/io/github/lostfly/map/GameMap.java
2D grid-based map with walls and floor tiles.
Key concepts:
- Tile size: 32x32 pixels
- 2D array
int[ROWS][COLS]where 0=floor, 1=wall - AABB collision detection (checking 4 corners of player rectangle)
Map generation:
- Fill with floor (0)
- Add border walls
- Place internal obstacles (L-shaped blocks)
Coordinate conversion:
public static int toCol(float x) { return (int)(x / TILE_SIZE); }
public static int toRow(float y) { return (int)(y / TILE_SIZE); }File: core/src/main/java/io/github/lostfly/entity/Hero.java
Player character with movement and collision detection.
Key concepts:
- SRP (Single Responsibility Principle): Hero only handles movement logic, not input
- Position stored in pixels (not grid cells)
- Collision checked independently for X and Y axes (allows "sliding" along walls)
Movement:
public void update(float dx, float dy, float delta, GameMap map) {
float newX = x + dx * SPEED * delta;
float newY = y + dy * SPEED * delta;
// Check X collision
if (!collidesWithMap(newX, y, map)) x = newX;
// Check Y collision
if (!collidesWithMap(x, newY, map)) y = newY;
}Collision detection:
- Check all 4 corners of hero's bounding box
- If any corner hits a wall → collision
File: core/src/main/java/io/github/lostfly/ui/Joystick.java
On-screen joystick for touch/mouse input using libGDX Scene2D.
Key concepts:
Touchpad- built-in Scene2D widgetStage+ScreenViewport- UI layer independent of game camera- Procedural textures via
Pixmap(no PNG files needed)
Input combination:
// Keyboard input
float dx = 0, dy = 0;
if (Gdx.input.isKeyPressed(Keys.A)) dx -= 1;
// ... other keys
// Add joystick input
dx += joystick.getKnobX();
dy += joystick.getKnobY();
// Normalize (diagonal shouldn't be faster)
float len = (float) Math.sqrt(dx * dx + dy * dy);
if (len > 1f) { dx /= len; dy /= len; }git log --oneline --graph --all| Commit | Description |
|---|---|
5df73e4 |
refactor: Updated Java version 8→21, renamed application |
9549962 |
Merge: feature/virtual-joystick |
7ce64f4 |
feat: Virtual joystick (Scene2D Touchpad) - added Joystick class with procedural textures |
4295417 |
Merge: feature/hero-entity |
44d87e3 |
feat: Hero entity with movement and collision detection |
# Show detailed changes for a specific commit
git show <commit-hash>
# Example: see joystick implementation
git show 7ce64f4
# See changes between two commits
git diff 44d87e3..7ce64f4git log --stat --onelineGameApp (extends Game)
│
├── SpriteBatch (shared)
│
└── Screen Manager
│
├── MenuScreen
│ ├── BitmapFont
│ └── GlyphLayout
│
└── GameScreen (extends ScreenAdapter)
├── OrthographicCamera
├── FitViewport
├── ShapeRenderer
├── BitmapFont (HUD)
├── GameMap (tile-based world)
├── Hero (player entity)
└── Joystick (Scene2D Touchpad)
# Run the game
./gradlew lwjgl3:run
# Build JAR
./gradlew lwjgl3:jar
# Clean build
./gradlew clean
# Refresh dependencies
./gradlew --refresh-dependencies
# Generate Eclipse project
./gradlew eclipse
# Generate IntelliJ project
./gradlew ideaEducational project for learning libGDX and game development.