A 2D game engine for Java — a message-driven entity/component core wrapped around a rigid-body physics and collision engine you can drop into a Swing window.
flowchart TD
World["World (extends GameLoop)<br/>fixed-timestep loop + input"] -->|"broadcast(Message)"| Bus["GameSystemManager<br/>message bus"]
Bus --> Render["RenderSystem<br/>Camera2D · Java2D"]
Bus --> Physics["PhysicsSystem<br/>gravity · air friction · sleeping"]
Physics --> Broad["Quadtree<br/>broad-phase"]
Physics --> Narrow["Collisions<br/>SAT + circle/rect narrow-phase"]
Physics --> Solver["Constraints<br/>distance · spring · string · angle"]
Render --> ECS["GameEntity + CollisionComponent"]
Physics --> ECS
Shapes["Shapes: CircleShape · RectShape"] --> Narrow
Ray["RaycastDetector"] --> Shapes
Res["ResourceManager<br/>images · text"] -.-> Render
Math["Vec2D · MathUtils"] -.-> Physics
GameLib is built around one idea: a fixed-timestep GameLoop drives a World, and everything else is a GameSystem reacting to broadcast Messages. Game objects are GameEntity bags of components; the bundled PhysicsSystem and RenderSystem do the heavy lifting — impulse-based collision response, a quadtree broad-phase, joints and constraints, sleeping bodies, and raycasting.
- Message-driven ECS —
GameSystemManagerbroadcasts typedMessages to registeredGameSystems; entities are composable bags ofGameComponents, so game logic, physics, and rendering stay decoupled. - Fixed-timestep loop —
GameLoopdecouples update rate (UPS) from draw rate (FPS) with an accumulator, and queues AWT keyboard and mouse input for you. - Rigid-body physics — gravity, air friction, per-
Materialrestitution and friction, and sleeping bodies, resolved with impulse-based collision response. - Collision detection —
Quadtreebroad-phase plus a SAT and specialized circle/rect narrow-phase that produces aCManifold; circle and AABB raycasting viaRaycastDetector. - Constraints & joints — distance, spring, string, angle, and point-body constraints, solved over several iterations per frame for stability.
- Rendering & resources — a Java2D
RenderSystemwith a pannableCamera2D, an immutableVec2Dmath type, and a cachingResourceManagerfor images and text.
import java.awt.*;
import java.awt.event.*;
import java.util.Queue;
import draw.RenderSystem;
import game.World;
import game.entity.GameEntity;
import game.messaging.*; // CreateEntityMessage, RenderMessage, UpdateMessage
import math.Vec2D;
import physics.*; // CollisionComponent, Material, PhysicsSystem
import physics.collision.shape.CircleShape;
/** A window where every mouse click drops a bouncing rubber ball. */
public class BouncingBalls extends World {
public BouncingBalls(Dimension bounds) {
super(60, 120, bounds); // cap drawing at 60 FPS, step physics at 120 UPS
systemManager.addSystem(new RenderSystem(systemManager, this));
systemManager.addSystem(new PhysicsSystem(systemManager,
new Rectangle(0, 0, bounds.width, bounds.height)));
}
private void dropBall(int x, int y) {
CollisionComponent ball = new CollisionComponent();
ball.setPos(new Vec2D(x, y));
ball.setShape(new CircleShape(10));
ball.setMass(10);
ball.setMaterial(Material.RUBBER);
GameEntity entity = new GameEntity();
entity.addComponent(ball);
systemManager.broadcast(new CreateEntityMessage(entity)); // physics + render pick it up
}
@Override public void update(float dt) { systemManager.broadcast(new UpdateMessage(dt)); }
@Override public void draw(Graphics2D g) { systemManager.broadcast(new RenderMessage(g)); }
@Override
public void processInput(Queue<EventPair<KeyEvent, KeyEventType>> keys,
Queue<EventPair<MouseEvent, MouseEventType>> mouse, Queue<MouseWheelEvent> wheel) {
for (EventPair<MouseEvent, MouseEventType> e : mouse)
if (e.type == MouseEventType.CLICK) dropBall(e.event.getX(), e.event.getY());
}
public static void main(String[] args) {
new BouncingBalls(new Dimension(800, 600)).createFrame().run();
}
}GameLib is an Eclipse Java 8 project — source in GameLib/src, tests in GameLib/test, and the SLF4J jars in GameLib/dependencies. Import it via File → Import → Existing Projects into Workspace, or build from the command line:
cd GameLib
# compile the library
javac -cp "dependencies/*" -d bin $(find src -name "*.java")
# compile and run the bundled demo: a rope of linked circles; click to drop steel balls
javac -cp "dependencies/*:bin" -d bin test/test/Test.java
java -cp "bin:dependencies/*:res" test.TestThe World (a GameLoop) advances physics in fixed dt steps and repaints a Swing DrawingPanel, broadcasting an UpdateMessage and a RenderMessage each frame. Systems subscribe by returning the Message classes they accept from getAcceptedMessages(). Each tick the PhysicsSystem rebuilds its Quadtree for broad-phase, runs narrow-phase collision detection plus constraint solving over several iterations, and puts low-energy bodies to sleep so idle stacks stay cheap. Because systems only ever talk through the message bus, you can add your own GameSystem without touching the rest of the engine.
Tech: Java 8 · SLF4J · Swing/Java2D. Built as a learning project — not published to a package registry.