Jump to content

Pixel Perfect Collision Detection (USE FOR YOUR JAVA GAMES!)

- - - - -

  • Please log in to reply
2 replies to this topic

#1
TrolliOlli

TrolliOlli

    Newbie

  • Members
  • Pip
  • 1 posts
Code NEW and only partially tested, if you spot any errors/inconsistencies please let me know.

So it took me several days to figure this out simply because there doesn't seem to be any real accurate guides for this out there. I did finally find an extremely helpful tutorial Here and worked off this.


Why go through all this trouble for pixel perfect collision detection? Simple, if you're making games with dynamic objects or really any object that doesn't have simplistic shape, it's very hard to work with basic collisions as you'd be using the borders of the image itself (the transparent borders usually) instead of the actual pixels in the image. With my method, collision will only be triggered upon pixels touching, and not simply on transparent borders touching.


First, the Code: *TIP* If you want to know where all the logic behind detecting whether or not collision is possible, see the previously referenced tutorial HERE


	// returns a HashSet of strings that list all the pixels in an image that aren't transparent

	// the pixels contained in the HashSet follow the guideline of:

	// x,y where x is the absolute x position of the pixel and y is the absolute y position of the pixel

	public HashSet<String> getMask(GameObject go){

		

		HashSet<String> mask = new HashSet<String>();

		BufferedImage image = null;

		try {

			image = ImageIO.read(new File (go.getDefaultImageLocation()));

		} catch (IOException e) {

			System.out.println("error");

		}

		int pixel, a;

		for(int i = 0; i < image.getWidth(); i++){ // for every (x,y) component in the given box, 

			for( int j = 0; j < image.getHeight(); j++){

		

		pixel = image.getRGB(i, j); // get the RGB value of the pixel

		a= (pixel >> 24) & 0xff;

		

		if(a != 0){  // if the alpha is not 0, it must be something other than transparent

			mask.add((go.xPos+i)+","+(go.yPos- j)); // add the absolute x and absolute y coordinates to our set

		}

			}

		}

		

		return mask;  //return our set

		

	}


// Returns true if there is a collision between object a and object b	

	public boolean checkCollision(GameObject a, GameObject b){

		

		// This method detects to see if the images overlap at all. If they do, collision is possible

		int ax1 = a.getxPos();

		int ay1 = a.getyPos();

		int ax2 = ax1 + a.getWidth();

		int ay2 = ay1 + a.getHeight();

		int bx1 = b.getxPos();

		int by1 = b.getyPos();

		int bx2 = bx1 + b.getWidth();

		int by2 = by1 + b.getHeight();

		

		if(by2 < ay1 || ay2 < by1 || bx2 < ax1 || ax2 < bx1)

		{

			return false; // Collision is impossible.

		}

		else // Collision is possible.

		{

			// get the masks for both images

			HashSet<String> maskPlayer1 = getMask(player1);

			HashSet<String> maskPlayer2 = getMask(player2);

		

			maskPlayer1.retainAll(maskPlayer2);  // Check to see if any pixels in maskPlayer2 are the same as those in maskPlayer1

			

			if(maskPlayer1.size() > 0){  // if so, than there exists at least one pixel that is the same in both images, thus

				System.out.println("Collision" + count);//  collision has occurred.

				count++;

				return true;

				

				}

			}

			return false;	

		}


	

Pseudo code for how this works:
- Anytime 2 images overlap, there is a possibility of collision and collision detection is triggered
- A mask is created for each image, the mask simply logs the coordinates of all non-transparent pixels
- The two masks are compared
- if the two masks share any similar values than collision is triggered


Notes on my code:
GameObject simply refers to the class I set up to control all game objects. Some methods I call during this code include .getWidth(), .getHeight(), and .getDefaultImageLocation(). Both .getWidth() and .getHeight() simply return the width/height of the Image used for the GameObject. In order to set up a BufferImage I needed an image location so I set up a .getDefaultImageLocation() method which simply returns a string value of the default image location (such as "images/player1/player1Default.png").

As I was so excited to finally have this working, I didn't take a whole lot of time to clean up the code or fully test it. If you see any room for improvement or any potential errors, PLEASE LET ME KNOW!

All feedback is appreciated, and I hope I've helped someone out there who, like me, wasn't satisfied with simple (if box1 is touching box2) { collision;}.

#2
lethalwire

lethalwire

    while(false){ ... }

  • Members
  • PipPipPipPipPipPipPip
  • 748 posts
  • Programming Language:Java, PHP
  • Learning:Java, PHP
I'll probably look at this within the next week or 2. I have been reading up on the LWJGL and I was considering writing a simple asteroids game to test what I've learned.
So you can see how and why this tutorial will come in handy.

#3
Sinipull

Sinipull

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 386 posts
You know, they usually don't test for collision pixel by pixel, because it's extremely inefficient. Larger shapes and trigonometry allows to check for collision with certain abstraction. Also, you should modify it to tell exactly where the collision happens.
.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users