Pixel Perfect Collision problems

Started by
11 comments, last by rip-off 15 years, 2 months ago
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; } } } }
Advertisement
Well, for starters, your pixel collision detection code does not take into account the relative position between the two sprites.
-----------------------------------------Everyboddy need someboddy!
Ok, and what is that supposed to mean?
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.
-----------------------------------------Everyboddy need someboddy!
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.
You do this after the chain of ifs, when the values of AB are already set.

Take a look at this picture:
Image Hosted by ImageShack.us


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...
-----------------------------------------Everyboddy need someboddy!
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. :(
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.
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.
"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.

btw, if you would please hand me some info about your way of collision testing I would be glad.

This topic is closed to new replies.

Advertisement