Advertisement

Advanced Collision Detection

Started by February 20, 2000 11:23 AM
8 comments, last by Zombie 25 years ago
My program uses many round shapes, and some odd shapes which I have to check if they collide (with mouse cursor). I have to get more accurate collision detection than just checking rects. How is that done? Is there something to do with colors?
One way is to create a "collision map." All you do is alloc a chunk of memory that can hold a black ''n'' white image (sort to speak) of your shape. On run-time, you can generate the image map (what is a colourkey becomes white, what is not a colourkey becomes black). Then, when you check for collisions, you just check to see if your cursor hits the black spot on the collision map. If it does, collision.

This is basically how to do it minus all the optimizations, etc. You can save mem by using bits ''stead of bytes for example.

Hope this helps.

~Queasy.

PS: You''d want to do the rect-checking first, then do the collision map stuff, just to save some cpu power.
Jonathan Makqueasy gamesgate 88[email=jon.mak@utoronto.ca]email[/email]
Advertisement
Why not just check on the original surface if the point is a color key or not, instead of created more surfaces.
William Reiach - Human Extrodinaire

Marlene and Me


Gromit''s got the idea--
Use a bounding rectangle to determine if your mouse is over the image, then look into the image''s surface to see if you are actually touching a piece of the image.


-- Goodlife-----------------------------Those whom the gods would destroy, they first drive mad.--DirectX design team official motto
Is there an example somewhere about it? I don''t know how to do that
Well, your image has a width and a height, yes? So, if your image is at coordinates (x,y) then your basic bounding rectangle would be: left= x, top= y, right= x+width, bottom= y+height. If the point falls anywhere within that region on the screen then you know it''s hit the image. So, you test that particular pixel to see if it''s transparent or not.

Instead of just grabbing the pixel from the screen, you need to grab it from the image source itself. Now, in local image coordinates, the values of the bounding box now become 0,0,width,height respectively. So, you need to do a conversion on the screen coordinates of the pixel you want to test.So, let''s say your sprite is at screen coordinates (sprite_x=100,sprite_y=120) and the image is 64x64. Your bounding box would be:

left=100
top=120
right=164
bottom=184

So, let''s assume the point of collision with the cursor is at (cursor_x=145,cursor_y=145). In this case, the following is true:

cursor_x > left AND cursor_x < right
cursor_y > top AND cursor_y < bottom

Therefore, the point is within the image''s bounding box. Now, to check the pixel, we need to convert to local image coordinates. Remember, the top left of the image is (0,0). So, since sprite_x = 100, we must subtract 100 from the cursor_x value ( to find the top left, sprite_x - sprite_x = 0, sprite_y - sprite_y = 0).
So, in local image coordinates:

pixel_x = cursor_x(145) - sprite_x(100);
pixel_y = cursor_y(145) - sprite_y(120);

Now, if the pixel in the image at (pixel_x(45),pixel_y(25)) is transparent, no collision occured. Otherwise, you have a collision.

Doing this with DirectDraw requires locking the surface, so I think building a lookup table when you load the image is not a bad idea since locking is slow, then you can access the loockup table to get the pixel value rather than locking the surface each time.

HTH

Advertisement
This is how I did it- thanks to you! DXObject is my type which contains surf,desc, rect, that kind of stuff.

Lookup table isn''t clear to me. What is it, and how can I use it?
And this function has one problem- when I move cursor in the edge of the surface, it sees a color there. That shouldn''t happen. There isn''t one, of course. How that can be removed?

Public Function CollidePixel(dxo As DXObject, backbuffer As DirectDrawSurface7) As BooleanDim ddrval As Long, zcx As Single, zcy As SingleDim HexColor As Longzcx = cX - dxo.sRect.Left  ''(dxo.ddsd.lWidth - ScrDim.Width)zcy = cY - dxo.sRect.Top ''(dxo.ddsd.lHeight - ScrDim.Height)dxo.DDSurface.Lock dxo.sRect, dxo.ddsd, DDLOCK_WAIT, 0  ''lukitse pinta ddrval = dxo.DDSurface.GetLockedPixel(zcx, zcy) ''ota kursorin päällä olevan pikselin arvo ''drawsurf.SetLockedPixel cx + (dxo.ddsd.lWidth - ScrDim.Width), cY - (dxo.ddsd.lHeight - ScrDim.Height), "&H" & Hex(ddrval)dxo.DDSurface.Unlock dxo.sRect ''avaa pintaHexColor = "&H" & Hex(ddrval)If HexColor = colorkey Then backbuffer.DrawText intTextsX, intTextsY + intTextYStep * 15, "Transparent", False CollidePixel = FalseElse backbuffer.DrawText intTextsX, intTextsY + intTextYStep * 15, "Cursor in Color", False CollidePixel = TrueEnd Ifbackbuffer.DrawText intTextsX, intTextsY + intTextYStep * 16, zcx, Falsebackbuffer.DrawText intTextsX, intTextsY + intTextYStep * 17, zcy, Falsebackbuffer.DrawText intTextsX, intTextsY + intTextYStep * 18, dxo.sRect.Left & " " & dxo.sRect.Top, FalseEnd Function 
A lookup table is simply an array of precalculated values that can be accessed quickly. You use them is situations where you need to do several intensive calculations. They''re commonly used to store precalculated trig values.

In this case, since you have to lock every surface you want to test for collision, a lookuptable would be useful to store the image information. You create an array the same size as the image buffer (image_width * image_height) for each image. When you load the images, just loack the surface and copy each pixel into your new array. I''m not too experienced in VB, but in C/C++ you would use the memcpy function to copy an entire row at a time. Then, you can access the pixels in the lookup table the same way you would with the normal image, but you don''t need to lock any surfaces to do so.

Now, I see a function there in your code called DDSurface.GetLockedPixel, so I imagine that''s a VB specific thing. In that case, you wouldn;t acces the lookup table the same way. In essence, you''d want to use an array like this:

image_lookup[x][y];
or
image_lookup[y][x];

However you do that in VB.

As far as the problem you mentioned, I''m not sure I understand you. Are all of your transparent pixels returning the wrong color? Or just along the edge? If it''s the former, then the only thing I can see in your code would be the conversion of the return value from GetLockedPixel to hex. That''s just a guess though, without knowing the actual setup of your colorkey variable. Rather than printing "Transparent" and "Cursor in Color" to the screen, perhaps you should print the values of the pixel color and the color key to get a better idea of why it''s not working.
It''s just along the edge, perhaps the last pixel. The color seems to be random.
It happened that when the cursor went in 0 line of x or y, there was one more x line, -1. There were some weird random colors.
And when cursor was in the last x/y line (50 or 160 in this case), colors were found again. Just these short
check lines were required to terminate this problem.

If zcx <= 0 Then zcx = 0
If zcy <= 0 Then zcy = 0
If zcx >= dxo.ddsd.lWidth - 1 Then zcx = dxo.ddsd.lWidth - 1
If zcy >= dxo.ddsd.lHeight - 1 Then zcy = dxo.ddsd.lHeight - 1

This topic is closed to new replies.

Advertisement