Click here to Skip to main content
15,792,609 members
Articles / Programming Languages / C#

Collision - A C# Game, Part 3: Pixel Perfect Collision Detection

Rate me:
Please Sign up or sign in to vote.
4.38/5 (16 votes)
16 Apr 20023 min read 120.5K   2.2K   39   10
Finishing my attempt at a simple game in C#

Sample Image - Collision3.jpg


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 optimize 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.


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, normalize 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), 

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

        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)
                    return true;

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


    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.


  • 17th April, 2002: Initial version


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.

Written By
Software Developer (Senior)
Australia Australia
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 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 I worked for the owners for five years before leaving to get away from the travel, and spend more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft based stuff in C++ and C#, with a lot of T-SQL in the mix.

Comments and Discussions

GeneralMy vote of 5 Pin
Sri Harsha Chilakapati10-Feb-13 4:24
Sri Harsha Chilakapati10-Feb-13 4:24 
GeneralCheers Pin
Paul Evans29-Oct-03 13:04
Paul Evans29-Oct-03 13:04 
GeneralCollision Pin
Hayat24-May-03 15:23
Hayat24-May-03 15:23 
GeneralAnother idea Pin
James T. Johnson18-Apr-02 14:53
James T. Johnson18-Apr-02 14:53 
In the last part you've inadvertantly have mixed two different ideas for screen updates in a 2D game.

In the case of a game with the entire screen changing this technique is useless, but when only a few parts are updated it is more effecient to update only what needs to be updated.

Enter the Dirty Rectangles algorithm.

When a screen update is performed you draw only what needs updating, so if an asteroid moves 3 pixels to the left, you draw the background for that block, then draw the asteroid on top of it.

In your game I don't think this is possible, but if you have two graphics that have overlapping rectangles you would consolidate the whole process doing one big update instead of two updates.

I haven't implemented the idea yet to see what the speed advantages are, but it should be faster than double buffering.

Even faster would be page flipping, but we'll have to wait for DX9 to do that Smile | :)

James - former hobbyist game programmer

Simplicity Rules!
GeneralSpeed enhancement Pin
James T. Johnson18-Apr-02 9:54
James T. Johnson18-Apr-02 9:54 
GeneralRe: Speed enhancement Pin
Christian Graus18-Apr-02 9:57
protectorChristian Graus18-Apr-02 9:57 
GeneralRe: Speed enhancement Pin
James T. Johnson18-Apr-02 10:03
James T. Johnson18-Apr-02 10:03 
GeneralRe: Speed enhancement Pin
Christian Graus18-Apr-02 10:12
protectorChristian Graus18-Apr-02 10:12 
GeneralRGB vs BGR Pin
Blake Coverett17-Apr-02 23:04
Blake Coverett17-Apr-02 23:04 
GeneralRe: RGB vs BGR Pin
Christian Graus17-Apr-02 23:16
protectorChristian Graus17-Apr-02 23:16 

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

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