2D Collision detection.

So I noticed that a lot of people are struggling with collision detection. I thought I might be able to help you a little.
Today, 2013-12-12, I will post the part 1 of this tutorial.
Tomorrow, 20130-12-13, I will post the part 2 of this tutorial.

Part 1 of this tutorial will explain how to do collision in an array of tiles.
Part 2 of this tutorial will discuss the theory behind making collision with non-array collision. Only theory, no code.

If you want the code from this tutorial to work properly, you will need to add lwjgl.jar and lwjgl_util.jar files to your classpath.

The rendering code is really bad and deprecated and SHOULDN’T be used in any real game. (You can if it is enough for you)

So let’s get started…

PART 1 - COLLISION IN AN ARRAY OF TILES!
First things first, let’s create a class for general stuff of the game. That includes making game window and running the game loop at 60UPS (Updates Per Second). Pretty basic…

package org.javagaming.collisiontutorial;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

public class GameClass {

	public GameClass() {
		
		makeDisplay();
		initGL();
		
		loop();
		
		Display.destroy();
	}
	
	private void loop() {
		
		while(!Display.isCloseRequested()) {
			
			glClear(GL_COLOR_BUFFER_BIT);
			
			tick();
			render();
		
			
			Display.update();
			Display.sync(60);
			
		}
		
	}
	
	private void tick() {
		
		
	}
	
	private void render() {
		
		
	}
	
	private void makeDisplay() {
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
			Display.setVSyncEnabled(true); // Enable vsync, so that we don't get screen tearing.
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
		
	}
	
	private void initGL() {
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, Display.getWidth(), Display.getHeight(), 0, -1, 1);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}
	
	public static void main(String args[]) {
		new GameClass();
	}
	
}

Now we need to add some kind of method for rendering stuff. In this example I will make Render class which will render rectangles with specified color. Pretty basic lwjgl stuff.


package org.javagaming.collisiontutorial;

import java.awt.Color;

import static org.lwjgl.opengl.GL11.*;

public class Render {

	public static void render(float x, float y, float width, float height, Color color) {
		
		glBegin(GL_QUADS);
		
		// Conver color from 0-255 int range to 0-1 float range.
		glColor3f(1f / 255 * color.getRed(), 1f / 255 * color.getGreen(), 1f / 255 * color.getBlue()); 
		
		glVertex2f(x, y);
		glVertex2f(x + width, y);
		glVertex2f(x + width, y + height);
		glVertex2f(x, y + height);
		
		glEnd();
		
	}
	
}

Now I make a level class, which has an array of tiles. When filling the array, I make it so that 1/10 tiles would be rock tile, and the rest would be grass tile.
Render method will just run through all the tiles and render them.
Get tile method will return a tile with x, y in tile precision.

package org.javagaming.collisiontutorial;

import java.util.Random;

public class Level {

	public static final int TILE_SIZE = 16;

	public int width, height;
	public Tile[] tiles;

	public Level(int width, int height) {

		Random random = new Random();

		this.width = width;
		this.height = height;
		tiles = new Tile[width * height];

		for (int i = 0; i < tiles.length; i++) {

			if (random.nextInt(10) == 0) {
				tiles[i] = Tile.rockTile;
			} else {
				tiles[i] = Tile.grassTile;
			}

		}

	}

	public void render() {
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				getTile(x, y).render(x, y);
			}
		}
	}
	
	public Tile getTile(int x, int y) {
		if(x < 0 || x >= width || y < 0 || y >= height) return Tile.voidTile;
		
		return tiles[x + y * width];
	}

}

After making the level class, don’t forget to make an object of it in your gameclass and add call render method from gameclass, so that you can actually render the level.

Here is a tile class!
Tile is a class which will only store the color of the tile and a boolean whether the player can move on that tile or not.

GrassTile is a tile which has green color, and you’re able to move on it.
RockTile is a tile which has gray color, and you can’t move on it.
VoidTile is a tile which has black color, and you can’t move on it.

public static Tile grassTile = new Tile(new Color(0, 255, 0), true);
	public static Tile rockTile = new Tile(new Color(100, 100, 100), false);
	public static Tile voidTile = new Tile(new Color(0, 0, 0), false);
	
	public Color color;
	private boolean canmove;
	
	public Tile(Color color, boolean canmove) {
		this.color = color;
		this.canmove = canmove;
	}
	
	public void render(int x, int y) {
		Render.render(x * Level.TILE_SIZE, y * Level.TILE_SIZE, Level.TILE_SIZE, Level.TILE_SIZE, color);
	}
	
	public boolean canMove() {
		return canmove;
	}

At this point you should be able to see a grid of tiles. Some of them are gray. Next comes the player. He will have the update method, in which we will do the collision!
I don’t really want to explain stuff… Just work it out yourself. It is not that hard really :stuck_out_tongue:

package org.javagaming.collisiontutorial;

import java.awt.Color;

import org.lwjgl.input.Keyboard;

public class Player {

	public int width, height;
	
	public int x, y;
	
	private Level level;
	
	public Player(int width, int height, Level level) {
		this.width = width;
		this.height = height;
		this.level = level;
	}
	
	public void tick() {
		// Which direction should I move?
		boolean down = Keyboard.isKeyDown(Keyboard.KEY_S);
		boolean up = Keyboard.isKeyDown(Keyboard.KEY_W);
		boolean left = Keyboard.isKeyDown(Keyboard.KEY_A);
		boolean right = Keyboard.isKeyDown(Keyboard.KEY_D);
		
		int xa = 0;
		int ya = 0;
		
		if(down) ya += 1;
		if(up) ya -= 1;
		if(left) xa -= 1;
		if(right) xa += 1;
		
		int speed = 2;
		
		// Do it on 2 different axis.
		if(canMove(speed * xa, 0)) {
			x += speed * xa;
		}
		
		if(canMove(0, speed * ya)) {
			y += speed * ya;
		}
		
	}
	
	private boolean canMove(int xspeed, int yspeed) {
		// We will check 4 points for collision. Upper left, upper right, down right, down left.
		// We will also add the speed values.
		return canMoveP(x + xspeed, y + yspeed) && canMoveP(x + width + xspeed, y + yspeed) && 
				canMoveP(x + width + xspeed, y + height + yspeed) && canMoveP(x + xspeed, y + height + yspeed);
	}
	
	private boolean canMoveP(int x, int y) {
		// We devide to convert from pixel precision to tile precision.
		return level.getTile(x / Level.TILE_SIZE, y / Level.TILE_SIZE).canMove(); 
	}
	
	public void render() {
		Render.render(x, y, width, height, Color.red); 
	}
	
}

Now make a player object in your GameClass and add render and tick calls for the player. You’re done! Now you can move in a little level width ASDW!

So after we add level and player objects to GameClass, it looks like this. I also changed the size of the glOrtho call to make the level look bigger. From Display.getWidth(), Display.getHeight() changed to 360, 280.

package org.javagaming.collisiontutorial;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

public class GameClass {

	public Level level = new Level(10, 10);
	public Player player = new Player(8, 8, level);
	
	public GameClass() {
		
		makeDisplay();
		initGL();
		
		loop();
		
		Display.destroy();
	}
	
	private void loop() {
		
		while(!Display.isCloseRequested()) {
			
			glClear(GL_COLOR_BUFFER_BIT);
			
			tick();
			render();
		
			
			Display.update();
			Display.sync(60);
			
		}
		
	}
	
	private void tick() {
		
		player.tick();
		
	}
	
	private void render() {
		
		level.render();
		
		player.render();
		
	}
	
	private void makeDisplay() {
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
			Display.setVSyncEnabled(true); // Enable vsync, so that we don't get screen tearing.
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
		
	}
	
	private void initGL() {
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, 360, 280, 0, -1, 1);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}
	
	public static void main(String args[]) {
		new GameClass();
	}
	
}

I will make a part 2 of the tutorial tomorrow.

Here is an eclipse project, which contains source code.

http://www.mediafire.com/download/ocppa8attpp7dah/CollisionTutorial.zip

Hmm I didn’t know I couldn’t edit tutorial once posted…
Guess I will just add stuff in this post.

THERE ARE SOME BUGS IN THAT CODE. They are not “breaking” bugs, but they are little stuff that needs to be fixed. I will leave it up to you to correct them. You should be able to see what they are!

Nice tutorial. I’d noticed the exact same thing and I myself am pondering on whether to post a tutorial on simple collision detection and response. It’s less newb friendly but I might post my own later today or tomorrow because AFAIK, it’s 100% accurate whereas this method isn’t pixel perfect, especially if the sprite moves faster.

@trollwarrior1

You can edit posts by clicking

icon at the bottom right of your post.