This blog shows up how to set up a basic game in Java!

Tools

Here we only use the built in Java libraries AWT and Swing.

Overview

We create 4 classes. One class contains the game loop, and manages the game. A game loop essentially runs every couple of miliseconds to update the game state. Another class contains the player controller, which is how you can move around and jump. One class draws the ground, and the other class is for the obstacle.

Setup

Create a new directory for all of the java files, so that they can be part of the same package. The reason that this isn't done in a Notebook is because the classes depend on each other, so individual cells containing classes won't be able to run.

Playing/Testing

Run the Game.java file.

The Game Class

// My package is called JavaCode
package JavaCode;

// Necessary imports
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;

// Inheriting from JPanel using extends JPanel
// This way we can override the paint method. This is important so that the screen is cleared before it is repainted. Otherwise, we can't see movement!
public class Game extends JPanel {

	// Instantiating the player (ball)
	Controller controller = new Controller(this);
	// Instantiating the ground
	Ground ground = new Ground(this);


	public Game() {
		// Adding key listener to constructor. 
		// This allows for user input.
		addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {
			}

			@Override
			public void keyReleased(KeyEvent e) {
				controller.keyReleased(e);
			}

			@Override
			public void keyPressed(KeyEvent e) {
				controller.keyPressed(e);
			}
		});
		setFocusable(true);
	}
	
	// Game loop, simply calling the method for the player to move.
	private void move() {
		controller.move();
	}

	// Overriding the paint method.
	// Super.paint is needed since we repaint every 10 miliseconds.
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2d = (Graphics2D) g;
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		// Painting the ground and the player.
		controller.paint(g2d);
		ground.paint(g2d);
	}

	// Simple Game Over screen
	public void gameOver() {
		JOptionPane.showMessageDialog(this, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
		System.exit(ABORT);
	}

	// Creating the JFrame
	public static void main(String[] args) throws InterruptedException {
		JFrame frame = new JFrame("ADfastpages Game");
		// New Game Object
		Game game = new Game();
		frame.add(game);
		frame.setSize(300, 400);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		// The game loop. Causes the progression of things in the game.
		while (true) {
			game.move();
			game.repaint();
			// Things aren't updated every frame. They are updated every 10 ms.
			Thread.sleep(10);
		}
	}
}

The Controller Class

package JavaCode;

import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.Rectangle;


public class Controller {
	// x and y are used to locate the player in 2D space
	int x = 0;
    int y = 300;
	// vx and vy are used to move the player around in certain increments.
	int vx = 0;
    int vy = 1;
	// The dimensions of the player. These remain constant.
	// We need this for a hitbox.
	private static final int WIDTH = 30;
	private static final int HEIGHT = 30;
	private Game game;

	// Creating an obstacle object here. 
	// We do it in this class since obstacles should spawn relative to the player.
	Obstacle obstacle = new Obstacle(game);


	public Controller(Game game) {
		// This object is instantiated in the game class, so that the controller can contain a reference to the game state.
		this.game= game;
	}

	// Method to move the player
	public void move() {
		// Making sure we move within the boundaries.
		if (x + vx > 0 && x + vx < game.getWidth()-60)
			x = x + vx;
        if (y + vy > 0 && x + vy < game.getHeight()-60)
            y = y + vy;
		
		// Detecting collision so that the game can end. 
		// References the Game Over method contained in the game class.
		if (collisionObstacle()) {
			game.gameOver();
		}
		// Implementing realistic ground.
		if (y < game.getHeight() / 2) {
			if (collisionGround()) {
				vy = -1;
			}
		}
		else {
			// Stopping player if it hits the ground.
			if (collisionGround()) {
				vy = 0;
			}
			else {
				vy = 1;
			}

		}

	}

	// The paint method needs to paint both the player, as well as call the obstacle paint method to paint the obstacle.
	public void paint(Graphics2D g) {
		g.fillOval(x, y, WIDTH, HEIGHT);
		obstacle.paint(g, 100);

	}

	// Creating a hitbox for the player.
	public Rectangle getBounds() {
		return new Rectangle(x, y, WIDTH, HEIGHT);
	}

	// Making sure the player doesn't move without key being pressed.
	public void keyReleased(KeyEvent e) {
		vx = 0;
	}

	// Input movement
	public void keyPressed(KeyEvent e) {
		// A and D move the player side to side.
		if (e.getKeyCode() == KeyEvent.VK_A)
			vx = -2;
		if (e.getKeyCode() == KeyEvent.VK_D)
			vx = 2;
		// Jump functionality.
		if (vy == 0) {
			if (e.getKeyCode() == KeyEvent.VK_SPACE)
				do {
					y = y - 1;
				} 
				while (y > 250);
				vy = -1;
		}

	}

	// Method to detect collision with the ground.
	private boolean collisionGround() {
		return game.ground.getBounds().intersects(getBounds());
	}

	// Separate method to detect collision with an obstacle.
	// Needed since collision with different things should cause different results. 
	private boolean collisionObstacle() {
		return this.obstacle.getBounds().intersects(getBounds());
	}

	


}

The Obstacle Class

package JavaCode;

import java.awt.Graphics2D;
import java.awt.Rectangle;


public class Obstacle {

	// Rererence to the game object.
	private Game game;
	// Dimensions for obstacle hitbox.
    private static final int WIDTH = 20;
	private static final int HEIGHT = 20;
	// Updating x position to spawn at. 
    private int localx = 60;
	

	public Obstacle(Game game) {
		this.game= game;
	}

	// Paint method used to displat the ground.
	// Needs an input for the x position as well, since the obstacle shouldn't always spawn in the same place.
	public void paint(Graphics2D g, int x) {
        localx = x;
		g.fillRect(localx, 325, WIDTH, HEIGHT);
	}

	// Setting up a collider to detect collision.
	public Rectangle getBounds() {
		return new Rectangle(localx, 325, WIDTH, HEIGHT);
	}


}

The Ground Class

package JavaCode;

import java.awt.Graphics2D;
import java.awt.Rectangle;


public class Ground {

	// Rererence to the game object.
	private Game game;
	private static final int HEIGHT = 30;
	

	public Ground(Game game) {
		this.game= game;
	}

	// Paint method used to displat the ground.
	public void paint(Graphics2D g) {
		g.fillRect(0, game.getHeight() - 30, game.getWidth(), 30);
	}

	// Setting up a collider to detect collision.
	public Rectangle getBounds() {
		return new Rectangle(0, game.getHeight() - 30, game.getWidth(), HEIGHT);
	}


}