/home/jeky

Guide to write a 2D game engine (1)

2017-12-02

What is a game engine? It is a framework or a library. Developers can use it to create games without knowing what is behind it. You don't need to know how graphic drivers render images to screen or how to compute the speed of a dropping ball. All you need to do is to tell the engine to render images or add gravity to this ball.

In this series of articles, I will show you how to write a 2D game engine in Java. I will only show the ideas and won't touch the details (such as optimization) behind.

How to render images/paint in Java

There are many UI libraries in Java, such as Swing, AWT, SWT or JavaFX. We will use AWT & Swing as they are easy to understand and we only need a canvas-like widget (not the Canvas.java). Here is a working example of rendering a blue rectangle:

import javax.swing.*;
import java.awt.*;

public class BlueRectangle extends JComponent {

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // fill background with black
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        // fill rectangle with blue
        g.setColor(Color.BLUE);
        g.fillRect(100, 100, 50, 50);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.add(new BlueRectangle());
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

First we create a subclass of JComponent.java and override the method paintComponent() in order to have a canvas widget. We can write all the painting statements in paintComponent(). This method will be invoked once a repaint event is triggered by user or system. In future we will show how to dynamically add these painting lines.

After we create this widget class, we need to add it into a top-container to show it. In Swing there are several top-containers: JWindow, JFrame and JDialog. Here we just use the most simple one, JFrame.

To paint this blue rectangle, we need a painter. In paintComponent() method, system will pass in a Graphics parameter (it is Graphics2D in fact), which can be used as a painter. You can use methods such as fillRect() or drawImage().

Run this code and you will get:

BlueRectangle

Now let us make it move

Now we want to add some animations. Just like film, animation in computer is to draw many images with a little bit difference between them, and then render them one by one in a short time. We call these images, Frames. Since we are already able to draw something, now we need to know how to update the frames. In Java, we can create a thread to redraw everything.

public class Game implements Runnable {

    public static final int DEFAULT_FPS = 60;

    private long timeDelta;
    private GameCanvas canvas;
    private boolean start;

    public Game(int fps) {
        this.timeDelta = (long) 1000 / fps;
        this.canvas = new GameCanvas(); // we will talk about this later
    }

    public void start() {
        start = true;
        new Thread(this).start();
    }

    public void stop() {
        start = false;
    }

    public void update() {
        // ... game logic here
    }

    @Override
    public void run() {
        while (start) {
            game.update();
            canvas.repaint();
            try {
                Thread.sleep(timeDelta);
            } catch (InterruptedException e) {
                start = false;
            }
        }
    }
}

A class implementing Runnable can be loaded into a Thread. You can create a thread with this class and call start() method to start a new thread so that the main Thread won't be blocked. In this thread (usually we call it a game thread or a update thread), the canvas will be repainting time after time.

The main game loop usually contains two parts:

In order to extract the changeable code out of this framework, we create an interface to do this:

import java.awt.Graphics2D;

public interface Animation {

    void update();

    void draw(Graphics2D g2d);
}

So we can have lots of classes implementing this interface and add the instances of them into a list.

Runnable code

I created a repo on github JAM to include all the code and runnable demos. This repo is split into two modules:

I will add more demos or tiny games as modules into this repo.

You can view all the code mentioned in this article on this commit. There are 5 classes added:

After run the demo code, you will see this:

Tiny Balls