Public Group

# Pixel Perfect Collision problems

This topic is 3405 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi all! I'm working on a 2D-engine using SDL. It's going pretty well, except i don't get my pixel-perfect collision detection to work properly. I know that pixel-perfect collision are expensive, and I won't use it for every collision. I still want it to work though. First I do a simple boundingbox-collision test. If there is a collision, I jump to checkPixelPerfectCollision(). I have the theory straight I think, but the problem seems to be (at least I think) that when I try to get the color value of a certain pixel, it gives me a completely false RGB-value. I guess my algorithm for getting the pixel is wrong. Here's the code anyway... please help me :'< bool CEntity::checkPixelPerfectCollision(CEntity* obj) { SDL_Rect A; A.x = m_X; A.y = m_Y; A.w = m_Width; A.h = m_Height; SDL_Rect B; B.x = obj->getX(); B.y = obj->getY(); B.w = obj->getWidth(); B.h = obj->getHeight(); SDL_Rect AB; if(A.x > B.x) { AB.x = A.x; AB.w = (B.x + B.w) - A.x; } else { AB.x = B.x; AB.w = (A.x + A.w) - B.x; } if(A.y > B.y) { AB.y = A.y; AB.h = (B.y + B.h) - A.y; } else { AB.y = B.y; AB.h = (A.y + A.h) - B.y; } int numberOfPixels = AB.w * AB.h; SDL_Surface* tmpA; SDL_Surface* tmpB; tmpA = SDL_CreateRGBSurface(SDL_HWSURFACE, AB.w, AB.h, 32, 0, 0, 0, 0); tmpB = SDL_CreateRGBSurface(SDL_HWSURFACE, AB.w, AB.h, 32, 0, 0, 0, 0); SDL_SetColorKey(tmpA, SDL_SRCCOLORKEY, SDL_MapRGB(tmpA->format, 255, 0, 255)); SDL_SetColorKey(tmpB, SDL_SRCCOLORKEY, SDL_MapRGB(tmpB->format, 255, 0, 255)); SDL_BlitSurface(m_Sprite->getBitmap(), &AB, tmpA, NULL); SDL_BlitSurface(obj->getSprite()->getBitmap(), &AB, tmpB, NULL); Uint32* transparent = (Uint32*)SDL_MapRGB(tmpA->format, 255, 0, 255); Uint8 r = 0; Uint8 g = 0; Uint8 b = 0; Uint8 r1 = 0; Uint8 g1 = 0; Uint8 b1 = 0; for(int y = 1; y < numberOfPixels; y++) { for(int x = 1; x < numberOfPixels; x++) { //Uint32* pixelA = (Uint32*)(tmpA->pixels + y * tmpA->pitch + x * 2); Uint32* pixelB = (Uint32*)(tmpB->pixels + y * tmpB->pitch + x * 2); Uint32* pixelA = (Uint32*)(tmpA->pixels + y * tmpA->pitch + x * 2); SDL_GetRGB(*pixelA, SDL_GetVideoInfo()->vfmt, &r, &g, &b); SDL_GetRGB(*pixelB, SDL_GetVideoInfo()->vfmt, &r1, &g1, &b1); Uint32 *pxA = (Uint32*)SDL_MapRGB(tmpA->format, r, g, b); Uint32 *pxB = (Uint32*)SDL_MapRGB(tmpB->format, r1, g1, b1); if(pxA != transparent && pxB != transparent) { return true; } else { return false; } } } }

##### Share on other sites
Well, for starters, your pixel collision detection code does not take into account the relative position between the two sprites.

##### Share on other sites
Ok, and what is that supposed to mean?

##### Share on other sites
Well, take a look at those lines:
SDL_BlitSurface(m_Sprite->getBitmap(), &AB, tmpA, NULL);
SDL_BlitSurface(obj->getSprite()->getBitmap(), &AB, tmpB, NULL);

You draw both sprites at the same position on tmpA and tmpB. You do your calculations as if both sprites' x and y positions are the same - and usually they aren't.

You should do something like this:
SDL_Rect relativeA;
relativeA.x=A.x-AB.x;
relativeA.y=A.y-AB.y;
SDL_Rect relativeB;
relativeB.x=B.x-AB.x;
relativeB.y=B.y-AB.y;
SDL_BlitSurface(m_Sprite->getBitmap(), &relativeA, tmpA, NULL);
SDL_BlitSurface(obj->getSprite()->getBitmap(), &relativeB, tmpB, NULL);

You can use the rects A and B instead of making new ones, but I gave them new names to make it easier to understand.

##### Share on other sites
Am I supposed to this in the chain of if:s when setting the AB-values, or after?
I don't really think I understand the idea.

##### Share on other sites
You do this after the chain of ifs, when the values of AB are already set.

Take a look at this picture:

The right part of the picture is the screen. The green area is the collision are between the bounding boxes of the two sprites, and is represented by AB. You calculate the relative position of A from the AB(notice that in this case, relativeA.x and relativeA.y are negative, because A is located up and left from AB). After that, you draw A to tmpA - the left part of the picture. You draw it at relativeA, so only the section of A that is colliding with B will be drawn to tmpA, and it will be draw at the correct location.

You do the same thing with B, and you'll have two SDL_Surfaces that represent the colliding sections of tmpA and tmpB. You know what to do from here...

##### Share on other sites
Allright! I think I understand now why to use these variables.
My other problem is that I allways get a junk value from SDL_GetRGB().
I want r, g and b to take the corresponding values of the currently selected pixel. But i always end up with something like (200, 59, 91). Odd.
Wouldn't it be nice if SDL stored each single pixel as a struct, holding x, y, and SDL_Color. :(

##### Share on other sites
You should pass the surface format to SDL_GetRGB:
Uint32* pixelA = (Uint32*)(tmpA->pixels + y * tmpA->pitch + x * 2);SDL_GetRGB(*pixelA, tmpA->format, &r, &g, &b);

Or you could use the getpixel/setpixel functions from the SDL documentation.

It is possible to do this without allocating additional surfaces.

##### Share on other sites
IMO, pixel perfect collision detection is more trouble than it's worth. Generating a few polygons for each object is easy to code, is much faster to execute, gives you much more information (penetration depth, collision normal) from which to perform collision response, and is almost as accurate.

##### Share on other sites
"IMO, pixel perfect collision detection is more trouble than it's worth. Generating a few polygons for each object is easy to code, is much faster to execute, gives you much more information (penetration depth, collision normal) from which to perform collision response, and is almost as accurate."

Yes, well thanks for the input, but as I mentioned in OP it is not a matter of saving processor-speed. I just want to learn how to make pixel perfect collision testing, because I think that it would be a nice thing to know.

• 10
• 40
• 15
• 10
• 23