Simple code review

I’m making a really simple game engine for myself. This is my first time really messing with Java’s graphical APIs so I was wondering if someone could go over my code to help find some things that might bite me in the ass later.

I’d also like to know if you guys think having nanosecond precision is worth the trouble or not. It seems to be very accurate with these timers:

delta: 7992652 sleep time: 7755883 (ms: 7 ns: 755883)
delta: 8001899 sleep time: 7723828 (ms: 7 ns: 723828)
delta: 7995118 sleep time: 7695471 (ms: 7 ns: 695471)
delta: 7951966 sleep time: 7838796 (ms: 7 ns: 838796)
delta: 8051832 sleep time: 7742630 (ms: 7 ns: 742630)
delta: 7884156 sleep time: 7682526 (ms: 7 ns: 682526)
delta: 8111936 sleep time: 7756192 (ms: 7 ns: 756192)
delta: 7936555 sleep time: 7828934 (ms: 7 ns: 828934)
delta: 8004056 sleep time: 7820919 (ms: 7 ns: 820919)

I just don’t know if it’s worth the time making everything based off of nanoseconds instead of mili.

GameCanvas:

package me.rabrg.apollo;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * 
 * @author Ryan Greene
 *
 */
@SuppressWarnings("serial")
public final class GameCanvas extends Canvas {

	private final BufferStrategy strategy;

	// TODO: add support for other container types
	protected GameCanvas(final JFrame container, final int width, final int height) {
		final JPanel panel = (JPanel) container.getContentPane();
		panel.setPreferredSize(new Dimension(width, height));
		panel.setLayout(null);
		
		setBounds(0, 0, width, height);
		panel.add(this);
		
		setIgnoreRepaint(true);
		
		container.pack();
		container.setResizable(false);
		container.setVisible(true);
		container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		requestFocus();
		
		createBufferStrategy(2);
		strategy = getBufferStrategy();
	}

	public BufferStrategy getStrategy() {
		return strategy;
	}

	public Graphics2D getGraphics2D() {
		return (Graphics2D) strategy.getDrawGraphics();
	}
}

Game:

package me.rabrg.apollo;

import java.awt.Color;
import java.awt.Graphics2D;

import javax.swing.JFrame;

/**
 * 
 * @author Ryan Greene
 *
 */
public abstract class Game {

	private static final long NANOSECONDS_PER_MILISECOND = 1000000;

	private final GameCanvas canvas;

	private final int tickRate;

	private boolean running;

	public Game(final String title, final int tickRate, final int width, final int height) {
		canvas = new GameCanvas(new JFrame(title), width, height);
		this.tickRate = tickRate;
	}

	public final void start() {
		running = true;
		initiate();
		
		long lastTickTime = System.nanoTime();
		long delta;
		long sleepTime;
		Graphics2D g;
		while (true) {
			delta = System.nanoTime() - lastTickTime;
			lastTickTime = System.nanoTime();
			g = canvas.getGraphics2D();
			
			update(delta);
			g.setColor(Color.BLACK);
			g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
			render(g);
			
			g.dispose();
			canvas.getStrategy().show();

			sleepTime = tickRate - (System.nanoTime() - lastTickTime);
			System.out.println("delta: " + delta + " sleep time: " + sleepTime + " (ms: " + sleepTime / NANOSECONDS_PER_MILISECOND + " ns: " + sleepTime % NANOSECONDS_PER_MILISECOND + ")");
			if (sleepTime > 0) {
				try {
					Thread.sleep(sleepTime / NANOSECONDS_PER_MILISECOND, (int) (sleepTime % NANOSECONDS_PER_MILISECOND));
				} catch (final InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println("[WARNING]: Load: " + (100 + Math.abs(sleepTime) / (tickRate / 100)) + "%!");
			}
		}
	}

	public final void stop() {
		running = false;
	}

	public final boolean isRunning() {
		return running;
	}

	public abstract void initiate();

	public abstract void update(final long delta);

	public abstract void render(final Graphics2D g);
}

TestGame:

import java.awt.Color;
import java.awt.Graphics2D;

import me.rabrg.apollo.Game;

/**
 * 
 * @author Ryan Greene
 *
 */
public final class TestGame extends Game {

	private float x;

	private float y;

	private float dx;

	private float dy;

	public TestGame() {
		super("Test Game", 1000000 * 8, 1280, 720);
	}

	public static void main(final String[] args) {
		new TestGame().start();
	}

	@Override
	public void initiate() {
		x = 0;
		y = 0;
		dx = 0.0000002F;
		dy = 0.0000002F;
	}

	@Override
	public void update(final long delta) {
		x += delta * dx;
		y += delta * dy;
	}

	@Override
	public void render(final Graphics2D g) {
		g.setColor(Color.RED);
		g.drawRect((int) x, (int) y, 50, 50);
	}

}