Java Collision Detection With Polygons

Java Collision Detection With PolygonsI’ve been asked many times to create a program that handles Java collision detection with polygons. This is a continuation of my tutorial on how to make a Java video game and if you haven’t been watching you should check it out.

It is surprisingly easy to handle collision detection with polygons. All you have to do is surround all of your polygons with rectangles and a handy dandy method named intersects handles pretty much everything else!

All the code follows the video.

If you like videos like this, please tell Google by clicking here

Sharing is also appreciated

Code from the Video

GameBoard.java

// Layout used by the JPanel
import java.awt.BorderLayout;

// Define color of shapes
import java.awt.Color;

// Allows me to draw and render shapes on components
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;

// Will hold all of my Rock objects
import java.util.ArrayList;

// Runs commands after a given delay
import java.util.concurrent.ScheduledThreadPoolExecutor;

// Defines time units. In this case TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class GameBoard extends JFrame{
	
	// Height and width of the game board
	
	public static int boardWidth = 1000;
	public static int boardHeight = 800;
	
	// Used to check if a key is being held down
	
	public static boolean keyHeld = false;
	
	// Gets the keycode for the key being held down
	
	public static int keyHeldCode;
	
	public static void main(String [] args)
    {
            new GameBoard();
            
    }
	
	public GameBoard()
    {
    	// Define the defaults for the JFrame
    	
        this.setSize(boardWidth, boardHeight);
        this.setTitle("Java Asteroids");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // Handles executing code based on keys being pressed
        
        addKeyListener(new KeyListener() {

			@Override
			public void keyTyped(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode()==87)
			    {
					System.out.println("Forward");
			    } else if (e.getKeyCode()==83){
			    	System.out.println("Backward");
			    } 
			    
				// Id the d key is pressed set keyHeld as if it
				// was being held down. This will cause the ship to 
				// constantly rotate. keyHeldCode stores the keyCode for d
			    
			    else if (e.getKeyCode()==68){
			    	System.out.println("Rotate Right");
			    	
			    	keyHeldCode = e.getKeyCode();
			    	keyHeld = true;
			    	
			    } 
			    
			    // Same thing is done here as was done with the last
				// 65 is the keyCode for a
			    
			    else if (e.getKeyCode()==65){
			    	System.out.println("Rotate Left");
			    	
			    	keyHeldCode = e.getKeyCode();
			    	keyHeld = true;
			    }
				
			}

			// When the key is released this informs the code that
			// the key isn't being held down
			
			public void keyReleased(KeyEvent e) {
		
				keyHeld = false;
				
			}
        	
        });

        
        GameDrawingPanel2 gamePanel = new GameDrawingPanel2();

     // Make the drawing area take up the rest of the frame
        
        this.add(gamePanel, BorderLayout.CENTER);
        
        // Used to execute code after a given delay
        // The attribute is corePoolSize - the number of threads to keep in 
        // the pool, even if they are idle
        
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
		
        // Method to execute, initial delay, subsequent delay, time unit
        
		executor.scheduleAtFixedRate(new RepaintTheBoard2(this), 0L, 20L, TimeUnit.MILLISECONDS);
        
        // Show the frame
        
        this.setVisible(true);
    }
	
}


// Class implements the runnable interface
// By creating this thread we can continually redraw the screen
// while other code continues to execute

class RepaintTheBoard2 implements Runnable{

	GameBoard theBoard;
	
	public RepaintTheBoard2(GameBoard theBoard){
		this.theBoard = theBoard;
	}

	@Override
	public void run() {
		
		// Redraws the game board
		
		theBoard.repaint();
		
	}
	
}

@SuppressWarnings("serial")

// GameDrawingPanel is what we are drawing on

class GameDrawingPanel2 extends JComponent { 
	
	// Holds every Rock I create
	
	public static ArrayList<Rock> rocks = new ArrayList<Rock>();
	
	// Get the original x & y points for the polygon
	
	int[] polyXArray = Rock.sPolyXArray;
	int[] polyYArray = Rock.sPolyYArray;
	
	// Create a SpaceShip
	SpaceShip theShip = new SpaceShip();
	
	// Gets the game board height and weight
	
	int width = GameBoard.boardWidth;
	int height = GameBoard.boardHeight;
	
	// Creates 50 Rock objects and stores them in the ArrayList
	// Suppress warnings when I clone the rocks array
	@SuppressWarnings("unchecked") // NEW
	public GameDrawingPanel2() { 
		
		for(int i = 0; i < 10; i++){
			
			// Find a random x & y starting point
			// The -40 part is on there to keep the Rock on the screen
			
			int randomStartXPos = (int) (Math.random() * (GameBoard.boardWidth - 40) + 1);
			int randomStartYPos = (int) (Math.random() * (GameBoard.boardHeight - 40) + 1);
			
			// Add the Rock object to the ArrayList based on the attributes sent
			
			rocks.add(new Rock(Rock.getpolyXArray(randomStartXPos), Rock.getpolyYArray(randomStartYPos), 13, randomStartXPos, randomStartYPos));
			
			Rock.rocks = rocks; // NEW
			
		}
		
	} 
	
	public void paint(Graphics g) { 
		
		// Allows me to make many settings changes in regards to graphics
		
		Graphics2D graphicSettings = (Graphics2D)g; 
		
		AffineTransform identity = new AffineTransform();
		
		// Draw a black background that is as big as the game board
		
		graphicSettings.setColor(Color.BLACK);
		graphicSettings.fillRect(0, 0, getWidth(), getHeight());
		
		// Set rendering rules
		
		graphicSettings.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
		
		// Set the drawing color to white
		
		graphicSettings.setPaint( Color.WHITE ); 
		
		// Cycle through all of the Rock objects
		
		for(Rock rock : rocks){
			
			// Move the Rock polygon 
			
			rock.move(); 
			
			// Stroke the polygon Rock on the screen
			
			graphicSettings.draw(rock); 
			
		} 
		
		// Handles spinning the ship in the right direction when the key
		// is pressed and held
		
		if(GameBoard.keyHeld == true && GameBoard.keyHeldCode == 68){
			
			SpaceShip.rotationAngle += 10;
			
		} else
			
			if(GameBoard.keyHeld == true && GameBoard.keyHeldCode == 65){
				
				SpaceShip.rotationAngle -= 10;
				
			}
		
		theShip.move();
		
		// Sets the origin on the screen so rotation occurs properly
		
		graphicSettings.setTransform(identity);
		
		// Moves the ship to the center of the screen
		
		graphicSettings.translate(GameBoard.boardWidth/2,GameBoard.boardHeight/2);
		
		// Rotates the ship
		
		graphicSettings.rotate(Math.toRadians(SpaceShip.rotationAngle));
		
		graphicSettings.draw(theShip);
		
		
	} 
	
}

Rock.java

import java.awt.Polygon;
import java.awt.Rectangle; // NEW
import java.util.ArrayList;

// Extending the Polygon class because I'm drawing Polygons

class Rock extends Polygon{
	
	// Upper left hand corner of the Polygon
	
	int uLeftXPos, uLeftYPos;
	
	// Used to change the direction of the asteroid when 
	// it hits something and determines how fast it moves
	
	int xDirection = 1;
	int yDirection = 1;
	
	// Define rock height and width
	
	int rockWidth = 26;
	int rockHeight = 31;
	
	// Copy of the Rock ArrayList
	// Holds every Rock I create
	
	static ArrayList<Rock> rocks = new ArrayList<Rock>();
	
	// For JApplet
	// int width = ExampleBoard.WIDTH;
	// int height = ExampleBoard.HEIGHT;
	
	// Get the board width and height
	
	int width = GameBoard.boardWidth;
	int height = GameBoard.boardHeight;
	
	// Will hold the x & y coordinates for the Polygons
	
	int[] polyXArray, polyYArray;
	
	// x & y positions available for other methods
	// There will be more Polygon points available later
	
	public static int[] sPolyXArray = {10,17,26,34,27,36,26,14,8,1,5,1,10};
	public static int[] sPolyYArray = {0,5,1,8,13,20,31,28,31,22,16,7,0};
	
	// Creates a new asteroid 
	
	public Rock(int[] polyXArray, int[] polyYArray, int pointsInPoly, int randomStartXPos, int randomStartYPos){
		
		// Creates a Polygon by calling the super or parent class of Rock Polygon
		
		super(polyXArray, polyYArray, pointsInPoly);
		
		// Randomly generate a speed for the Polygon
		
		this.xDirection = (int) (Math.random() * 4 + 1);
		
		this.yDirection = (int) (Math.random() * 4 + 1);
		
		// Holds the starting x & y position for the Rock
		
		this.uLeftXPos = randomStartXPos;
		
		this.uLeftYPos = randomStartYPos;
		
	}
	
	// NEW: Creates a bounding rectangle for collision checking
	
	public Rectangle getBounds() {
		
        return new Rectangle(super.xpoints[0], super.ypoints[0], rockWidth, rockHeight);
        
    }

	public void move(){
		
		// This rectangle surrounds the rock I'll check against
		// all of the other rocks below
		
		Rectangle rockToCheck = this.getBounds();
		
		// Cycle through all the other rocks and check if they
		// cross over the rectangle I created above
		
		for(Rock rock : rocks){
			
			// Creates a bounding rectangle that is used temporarily
			// for each other rock on the board
			
			Rectangle otherRock = rock.getBounds();
			
			// Check to make sure I'm not comparing one rock to itself
			// Check if one rock crosses over another rock
			
			if(rock != this && otherRock.intersects(rockToCheck)){

				// Switch the direction the rocks are moving on impact
				
				int tempXDirection = this.xDirection;
				int tempYDirection = this.yDirection;
				
				this.xDirection = rock.xDirection;
				this.yDirection = rock.yDirection;
				
				rock.xDirection = tempXDirection;
				rock.yDirection = tempYDirection;
				
			}
			
		} // END OF NEW STUFF
		
		// Get the upper left and top most point for the Polygon
		// This will be dynamic later on
		
		int uLeftXPos = super.xpoints[0]; 
		
		int uLeftYPos = super.ypoints[0];
		
		// If the Rock hits a wall it will go in the opposite direction
		
		if (uLeftXPos < 0 || (uLeftXPos + 25) > width) xDirection = -xDirection; 
		
		if (uLeftYPos < 0 || (uLeftYPos + 50) > height) yDirection = -yDirection;
		
		// Change the values of the points for the Polygon
		
		for (int i = 0; i < super.xpoints.length; i++){
			
			super.xpoints[i] += xDirection;
			super.ypoints[i] += yDirection;
			
		}
		
	}
	
	// public method available for creating Polygon x point arrays
	
	public static int[] getpolyXArray(int randomStartXPos){
		
		// Clones the array so that the original shape isn't changed for the asteroid
		
		int[] tempPolyXArray = (int[])sPolyXArray.clone();
		
		for (int i = 0; i < tempPolyXArray.length; i++){
			
			tempPolyXArray[i] += randomStartXPos;
			
		}
		
		return tempPolyXArray;
		
	}
	
	// public method available for creating Polygon y point arrays
	
	public static int[] getpolyYArray(int randomStartYPos){
		
		// Clones the array so that the original shape isn't changed for the asteroid
		
		int[] tempPolyYArray = (int[])sPolyYArray.clone();
		
		for (int i = 0; i < tempPolyYArray.length; i++){
			
			tempPolyYArray[i] += randomStartYPos;
			
		}
		
		return tempPolyYArray;
		
	}
	
}

SpaceShip.java

import java.awt.Polygon;
import java.awt.*;

@SuppressWarnings("serial")
public class SpaceShip extends Polygon{
	
	// Upper left hand corner of space ship
	
	static int uLeftXPos = 500, uLeftYPos = 400;
	
	// Center of space ship
	
	int centerX = 515;
	int centerY = 415;
	
	// Determines the direction the ship moves
	
	static int xDirection = 0;
	static int yDirection = 0;
	
	// Get the board width and height
	
	int width = GameBoard.boardWidth;
	int height = GameBoard.boardHeight;
	
	// Will hold the x & y coordinates for the ship
	// Everything is based on coordinates from the center
	// It is done this way so that rotation works properly
		
	public static int[] polyXArray = {-13,14,-13,-5,-13};
	public static int[] polyYArray = {-15,0,15,0,-15};
	
	// Defines if the ship should rotate
	
	static int rotationAngle = 0;
	
	// Holds the area of the ship polygon
	
	static double areaOfShip;
	
	// Creates a new space ship
	
	public SpaceShip(){
			
			// Creates a Polygon by calling the super or parent class of Rock Polygon
			
			super(polyXArray, polyYArray, 5);
			
			getShipArea();
			
			updateCenterPoints();
			
	}
	
	public void move(){
		
		// Get the upper left and top most point for the Polygon
		// This will be dynamic later on
		/*
		int uLeftXPos = super.xpoints[0]; 
		int uLeftYPos = super.ypoints[0];
		
		// If the ship hits a wall it will go in the opposite direction
		
		if (uLeftXPos < 0 || (uLeftXPos + 25) > width) xDirection = -xDirection; 
		
		if (uLeftYPos < 0 || (uLeftYPos + 50) > height) yDirection = -yDirection; 
		
		// Change the values of the points for the Polygon
		
		for (int i = 0; i < super.xpoints.length; i++){
			
			super.xpoints[i] += xDirection;
			super.ypoints[i] += yDirection;
			
		}
		*/
		
		super.xpoints = SpaceShip.polyXArray;
		super.ypoints = SpaceShip.polyYArray;
		
	}
	
	// This is here just as a demonstration. IT ISN'T USED
	
	public void rotateRight(){

		// Calculate the length of the line from 1 vector to the next
		// SQRT ((x1-x2)^2 + (y1-y2)^2)
		// Find the angle of the line from 1 vector to the next
		// x1*x2 + y1*y2 = C
		// SQRT ( x1^2 + x2^2 ) = A
		// SQRT ( y1^2 + y2^2 ) = B
		// ARCCOS ( C / A * B) = Angle of line
		// x2 = x1 + cos(angleOfLine) * length
		// y2 = y1 + sin(angleOfLine) * length
		
		// How to find new x & y points for the rotating polygon
		// This works on paper, but doesn't with Java because errors occur
		// because we have to convert floats into ints
		
		/*
		
		for(int i = 0; i < super.xpoints.length; i++){
			
			polyXArray[i] = this.centerX + (int) ((super.xpoints[i] - this.centerX) * cos(angle) -
					(super.ypoints[i] - this.centerY) * sin(angle));
			
			polyYArray[i] = this.centerY + (int) ((super.xpoints[i] - this.centerX) * sin(angle) +
					(super.ypoints[i] - this.centerY) * cos(angle));
			
		}
		*/
		
	}
	
	// How to find the ships area
	// This is here just as a demonstration. IT ISN'T USED
	
	public void getShipArea(){
		
		// Calculate the area of the polygon
				
		// Temporary sum holder
				
		double sumHolder1 = 0, sumHolder2 = 0;
				
		// Find area of polygon 
		// Multiply x coordinate of each vertex by the y coordinate
		// of the next vertex. Multiply y coordinate of each vertex 
		// by the x coordinate of the next vertex. Subtract these
		// sums and divide by 2 to get the polygons area
				
		// X Points {-13,14,-13,-5,-13}
		// Y Points {-15,0,15,0,-15}
		// -13 * 0
		// 	14 * 15 ... (Do the same for Y)
		
		for(int i = 0, n = 1; i < 4; i++, n++){
					
			sumHolder1 += super.xpoints[i] * super.ypoints[n];
					
		}
				
		for(int i = 0, n = 1; i < 4; i++, n++){
					
			sumHolder2 += super.ypoints[i] * super.xpoints[n];
					
		}
				
		areaOfShip = Math.abs(sumHolder1 - sumHolder2) / 2;
		
		System.out.println("Area of Ship: " + areaOfShip);
		
	}
	
	// How to find the ships center points
	// This is here just as a demonstration. IT ISN'T USED
	
	public void updateCenterPoints(){
		
		// Get the origin points
		// X Points {-13,14,-13,-5,-13}
		// Y Points {-15,0,15,0,-15}
		// Do the following for each row in array
		// (-13 + 14) * (-13 * 0) - (14 * -15)
		
		double xOrigin = 0, yOrigin = 0;
		
		// X Points {-13,14,-13,-5,-13};
		// Y Points {-15,0,15,0,-15};
		// Do the following for each row in array
		// (-13 + 14) * (-13 * 0) - (14 * -15)
		
		for (int j=0; j < 4; j++){

			xOrigin += (super.xpoints[j] + super.xpoints[j+1]) * ((super.xpoints[j]*super.ypoints[j+1]) - (super.xpoints[j+1]*super.ypoints[j]));
			yOrigin += (super.ypoints[j] + super.ypoints[j+1]) * ((super.xpoints[j]*super.ypoints[j+1]) - (super.xpoints[j+1]*super.ypoints[j]));
		}
		
		this.centerX = (int) (xOrigin/(6 * areaOfShip));
		
		this.centerY = (int) (yOrigin/(6 * areaOfShip));
		
		System.out.println("Center X: " + this.centerX);
		System.out.println("Center Y: " + this.centerY);
	}
	
}

6 Responses to “Java Collision Detection With Polygons”

  1. punit says:

    Great. this is what i always want…
    Thank you 🙂

  2. Daniel says:

    Thank you so much. I’ve been using various online resources to teach myself how to code, and yours have been the most helpful so far.

  3. Fahad says:

    can i upload a design from inkscape for example to a java game ?

Leave a Reply

Your email address will not be published.

Google+