Click here to Skip to main content
Licence 
First Posted 16 Apr 2002
Views 93,568
Bookmarked 32 times

Collision - A C# Game, part 3: pixel perfect collision detection

By | 16 Apr 2002 | Article
Finishing my attempt at a simple game in C#

Sample Image - Collision3.jpg

Goals

Having come down a fair way from my original lofty plans, I remain determined that collision detection in this game should be perfect, not approximated.  This can only be done by direct access to the bitmap data.  As I've found everything so far to be slower than I would like, my first step was to look to optimise what I had.

JPEG vs. Bitmap

You'll recall my original resources were all jpegs to save space, but that I noted this meant I had to specify a colour range when drawing in order to mask the background.  For the sake of speed I have gone to bitmap resources, which meant drawing the backgrounds to all be hard black by hand.  This means you may find the game does not appear pixel perfect, because some of the old blue background might still be there, but not visible in the game.  I assure you the technique I am showing you is not at fault, my job in editing the bitmaps is.  The code is hopefully better prepared for the cost of our collision detection, but you'll notice the serious jump in the size of the exe and the project.  No new bitmaps were added, that's just the cost of bitmaps as opposed to jpegs.

MakeTransparent

Having done this, I can use the MakeTransparent method of Bitmap, passing in Color.Black as the parameter, and I do not have to worry again about masking - the image will now automatically draw with the black areas masked. 

Per Pixel Collisions

Which leaves me with the collision code.  The first step is easy, that is, to check if we've collided by checking the planet Rectangle against that of the ship.  This takes place in the same loop as the moving of planets and score keeping, but I've removed that code for clarity ( you saw it last time ).

 for (int i = 0; i < m_arAsteroids.Count; ++i)
{
     if (arTemp.rcAsteroid.IntersectsWith(m_rcShipPos))
    {
        // We've hit
    }                        

But the problem is that as you recall, we have one huge planet, so the others all have a fair amount of transparency in them.  Even if this was not so, no game player would be happy to find they die if they come within the rectangle that defines the planet they are avoiding.  We need to do better.  The solution is to figure out the rectangle that defines the area shared by both objects, normalise that rectangle for both bitmaps and then step through them together to see if any pixel position in the intersected rectangle contains a pixel vale that is not masked in both bitmaps.  The main trick is to limit the area we step through as much as possible, because it's not a cheap operation.

 private bool HitTest(Asteroid a) 
{ 
    Rectangle rcIntersect = a.rcAsteroid; rcIntersect.Intersect(m_rcShipPos);
    BitmapData bmData = m_bmShip.LockBits(
                               new Rectangle(rcIntersect.X - m_rcShipPos.X, 
                                             rcIntersect.Y - m_rcShipPos.Y, 
                                             rcIntersect.Width, rcIntersect.Height), 
                               ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    BitmapData bmData2 = m_bmPlanets.LockBits(
                      new Rectangle(87 * a.nBitmap + rcIntersect.X - a.rcAsteroid.X, 
                                   rcIntersect.Y - a.rcAsteroid.Y, 
                                   rcIntersect.Width, rcIntersect.Height), 
                                   ImageLockMode.ReadOnly, 
                                   PixelFormat.Format24bppRgb);

    int stride = bmData.Stride;
    System.IntPtr Scan0 = bmData.Scan0;
    System.IntPtr Scan1 = bmData2.Scan0;

    unsafe
    {
        byte * p = (byte *)Scan0;
        byte * p1 = (byte *)Scan1;

        int nOffset = stride - rcIntersect.Width*3;

        for(int y=0;y<rcIntersect.Height;++y)
        {
            for(int x=0; x < rcIntersect.Width; ++x )
            {
                if (p[0] != 0 &&
                    p[1] != 0 &&
                    p[2] != 0 &&
                    p1[0] != 0 &&
                    p1[1] != 0 &&
                    p1[2] != 0)
                {
                    m_bmShip.UnlockBits(bmData);
                    m_bmPlanets.UnlockBits(bmData2);
                    return true;
                }

                p += 3;
                p1 += 3;;
            }
            p += nOffset;
            p1 += nOffset;
        }
    }

    m_bmShip.UnlockBits(bmData);
    m_bmPlanets.UnlockBits(bmData2);

    return false;            
}

So now we do our rectangle test first, then test this function, and if we get a true for both, we end the game.  A message box informs us of our score, and then when we close that, the game starts again, with the star field regenerated in a new pattern.

It's not very exciting ( the target audience was my 5 year old daughter ), but it achieved my personal goal of giving me an excuse to write some more C# code, and hopefully the topics I've covered along the way have been of interest to some fellow CPians.  I have an Asteroids game using DirectX on my hard drive somewhere, I will dig it up and write something about it so that a comparison can be made.  It's hardly a fair one, given that this stuff is not what C# is for, and I don't know that C++ without DirectX would have fared that much better.  I'd say C# made this easier to write by virtue of the integration of GDI+ which did a lot of the work for us. 

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Christian Graus

Software Developer (Senior)

Australia Australia

Member

Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and now I work for the new owners.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralCheers PinmemberPaul Evans12:04 29 Oct '03  
GeneralCollision Pinmemberhayat14:23 24 May '03  
GeneralAnother idea PinmemberJames T. Johnson13:53 18 Apr '02  
GeneralSpeed enhancement PinmemberJames T. Johnson8:54 18 Apr '02  
GeneralRe: Speed enhancement PinmemberChristian Graus8:57 18 Apr '02  
GeneralRe: Speed enhancement PinmemberJames T. Johnson9:03 18 Apr '02  
GeneralRe: Speed enhancement PinmemberChristian Graus9:12 18 Apr '02  
GeneralRGB vs BGR PinmemberBlake Coverett22:04 17 Apr '02  
GeneralRe: RGB vs BGR PinmemberChristian Graus22:16 17 Apr '02  
Microsoft refer to their internal format as BGR when it comes out in the format returned by GDI+, and GDI+ allows us to request RGB or BGR. So either GDI+ has it wrong, or Microsoft have changed their mind about what constitutes RGB, and what consititutes BGR.
 
I've not played with the ARGB version, largely because the format I've requested was the only only GDI+ would accept when I last tried under C++ to experiment with the different return values, even though it did not return it. I guess getting an ARGB return would be the only true way to settle if the format is coming out wrong, or if Microsoft have changed what they mean by BGR and RGB.

 
Christian
 
The tragedy of cyberspace - that so much can travel so far, and yet mean so little.
 
"But there isn't a whole lot out there that pisses me off more than someone leaving my code looking like they leaned on the keyboard and prayed that it would compile.
- Jamie Hale, 17/4/2002

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120529.1 | Last Updated 17 Apr 2002
Article Copyright 2002 by Christian Graus
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid