New Development on Old Projects

Posted by SkidRunner on April 8, 2015, 1:56 p.m.

Hello 64Digits It has been some time since I have created a new blog post. I have decided to start working on my Retro3D engine again I found a lot of things I would like to improve. I am going to start at the beginning and work my way back to where I was and hopefully fix the old issues as I go along.

So my old game loop wasn't that bad actually it performs better than most I have seen out there. I feel that it is good enough to share with the community. So for all you out there trying to create a game engine for your projects take a look and tell me what you think.

So the first thing I do that I don't see in most game loop snippets is I use a final AtomicBoolean instead of a volatile boolean. I do this because using an Atomic with synchronization is less likely to cause concurrency issues. You may also notice that while the isRunning method is public the setRunning method is not. This is because no outside class should be calling setRunning directly I have other methods for doing this.

private final AtomicBoolean running;

public final boolean isRunning() {
	boolean value = false;
	synchronized(running) {
		value = running.get();
	}
	return value;
}

private final boolean setRunning(boolean value) {
	synchronized(running) {
		running.set(value);
	}
}

Next I create a start and stop method that can be accessed externally. This will allow other classes to start and stop the game usually from your input manager or main window. So in the start method I check to see if the game is running if it is not I create a new instance of the game and start the gameThread. As you can see I use the setRunning method to do this and now you can also see why that method is private because there are a lot of other things that need to be executed as well. In the stop method I set running to false and wait for the thread to join if an error is encountered I keep trying, you would need to have some pretty big issues to get this stuck in an infinite loop.

private Thread gameThread;

public final void start() {
	if(!isRunning()) {
		setRunning(true);
		gameThread = new Thread(this);
		gameThread.start();
	}
}

public final void stop() {
	if(isRunning()) {
		setRunning(false);
		if(!Thread.currentThread().equals(gameThread)) {
			while(true) {
				try {
					gameThread.join();
					break;
				} catch(Exception exception) {
					/* Do Nothing */
				}
			}
		}
	}
}

No here is the bit you have been waiting for or your just really bored. The following allows the game to keep updating only rendering when it is needed to keep smooth animation. You can adjust the max_fps to up the frame rate if you so choose to. The class where all these methods live is an abstract class that extends Component and implements Runnable this is where the paint, run, getWidth, getHeight, and repaint methods are coming form. You will notice I never sleep the game thread that is because the buffer is synchronized so it can only be accessed by one thread at a time witch means no screen tearing.

public static final double one_nan = 1000000000;
public static final double max_fps = one_nan / 60;

private final BufferedImage buffer;

@Override
public void paint(Graphics graphics) {
	synchronization(buffer) {
		graphics.drawImage(buffer, 0, 0, getWidth(), getHeight(), null);
	}
}

@Override
public void run() {
	long updateEpoch = System.nanoTime();
	long renderEpoch = updateEpoch;
	while(isRunning()) {
		long updateElapse = System.nanoTime() - updateEpoch;
		updateEpoch += updateElapse;
		update(updateElapse / one_nan);
		long renderElapse = updateEpoch - renderEpoch;
		if(renderElapse > max_fps) {
			synchronization(buffer) {
				Graphics2D graphics = buffer.createGraphics();
				graphics.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
				render(graphics);
				graphics.dispose();
			}
			repaint();
			updateEpoch += renderElapse;
		}
	}
}

public abstract void update(double elapse);

public abstract void render(Graphics2D graphics);

Well that is all for now I hope this is useful to someone out there and hopefully this will lead to me actually completing a game.

Comments