views:

652

answers:

1

Hi everyone, I have a C# snippet code about sprite collision in C# game programming, hope you guys will help me to clarify it.

I dont understand method IsCollided, especially the calculation of d1 and d2 to determine whether the sprites collide or not, the meaning and the use of Matrix Invert() and Multiphy in this case as well as the use of Color alpha component to determine the collision of 2 sprites.

Thank you very much.

public struct Vector
{
   public double X;
   public double Y;

   public Vector(double x, double y)
   {
      X = x;
      Y = y;
   }

   public static Vector operator -(Vector v, Vector v2)
   {
      return new Vector(v.X-v2.X, v.Y-v2.Y);
   }

   public double Length
   {
      get
      {
         return Math.Sqrt(X * X + Y * Y);
      }
   }
}

public class Sprite
{
    public Vector Position;
    protected Image _Image; 
    protected Bitmap _Bitmap;
    protected string _ImageFileName = "";
    public string ImageFileName
    {
       get { return _ImageFileName; }
       set
       {
          _ImageFileName = value;
          _Image = Image.FromFile(value);
          _Bitmap = new Bitmap(value);
        }
     }

    public Matrix Transform
    {
      get
      {
            Vector v = Position;
            if (null != _Image)
               v -= new Vector(_Image.Size) / 2;

            Matrix m = new Matrix();
            m.RotateAt(50.0F, new PointF(10.0F, 100.0F));
            m.Translate((float)v.X, (float)v.Y);
            return m; 
       }
    }

    public bool IsCollided(Sprite s2)
    {
        Vector v = this.Position - s2.Position;
        double d1 = Math.Sqrt(_Image.Width * _Image.Width + _Image.Height * _Image.Height)/2;
       double d2 = Math.Sqrt(s2._Image.Width * s2._Image.Width + s2._Image.Height * s2._Image.Height)/2;
       if (v.Length > d1 + d2)
           return false;

        Bitmap b = new Bitmap(_Image.Width, _Image.Height);
        Graphics g = Graphics.FromImage(b);
        Matrix m = s2.Transform;

        Matrix m2 = Transform;
        m2.Invert();

        Matrix m3 = m2;
        m3.Multiply(m);

        g.Transform = m3;

        Vector2F v2 = new Vector2F(0,0);
        g.DrawImage(s2._Image, v2);

        for (int x = 0; x < b.Width; ++x)
           for (int y = 0; y < b.Height; ++y)
           {
              Color c1 = _Bitmap.GetPixel(x, y);
              Color c2 = b.GetPixel(x, y);

              if (c1.A > 0.5 && c2.A > 0.5)
                  return true;
            }

       return false;
    }
}
+2  A: 

It's a little convoluted. The two parts:

Part One (simple & fast)

The first part (involving v, d1 + d2) is probably confusing because, instead of doing collision tests using boxes, the dimensions of the image are used to construct bounding circles instead. A simple collision test is then carried out using these bounding circles. This is the 'quick n dirty' test that eliminates sprites that are clearly not colliding.

"Two circles overlap if the sum of there(sic) radii is greater than the distance between their centers. Therefore by Pythagoras we have a collision if:

(cx1-cx2)2 + (cy1-cy2)2 < (r1+r2)2"

See the "Bounding Circles" section of this link and the second link at the foot of this answer.

commented code:

// Get the vector between the two sprite centres
Vector v = this.Position - s2.Position;

// get the radius of a circle that will fit the first sprite
double d1 = Math.Sqrt(_Image.Width * _Image.Width + _Image.Height * _Image.Height)/2;

// get the radius of a circle that will fit the second sprite
double d2 = Math.Sqrt(s2._Image.Width * s2._Image.Width + s2._Image.Height * s2._Image.Height)/2;

// if the distance between the sprites is larger than the radiuses(radii?) of the circles, they do not collide
if (v.Length > d1 + d2)
       return false;

Note: You may want to considering using an axially aligned bounding box test instead of a circle here. if you have rectangles with disparate widths and lengths, it'll be a more effective/accurate first test.

Part Two (slower)

I haven't got time to check the code 100%, but the second part --having established that the two sprites are potentially colliding in step one-- is performing a more complicated collision test. It is performing a per-pixel collision test using the sprite's image source -- specifically its alpha channel. In games, the alpha channel is often used to store a representation of transparency. The larger the value, the more opaque the image (so 0 would be 100% transparent, 1.0f would be 100% opaque).

In the code shown, if the pixels in both sprites are overlapping and both have alpha values of > 0.5f, the pixels are sharing the same space and represent solid geometry and are thus colliding. By doing this test, it means that transparent (read: invisible) parts of the sprite will not be considered when testing for collisions, so you can have circular sprites that do not collide at the corners etc.

Here's a link that covers this in a bit more detail.

Mark Simpson
Unless the size of the sprite's image changes, the quick and dirty test would be a lot quicker if it didn't calculate the size of the bounding circle every time it was called.
Robert Rossney
You can just as well compare the spheres in squared-space like this: (cx1 - cx2) + (cy1 - cy2) < (r1 + r2); saving the overhead of calculating the square-roots everytime.
Jasper Bekkers
Jasper: Yup, definitely :) I'll add that to the answer when I have a moment
Mark Simpson
Thank u guys very much. You enlightened me a lot, but I still dont understand why here they use inverse matrix, what is the role of inverse matrix in this case as well as in sprite animation, since I saw they using the inverse matrix of parent sprite and then multiply it by the child sprite's matrix quite a lot in the whole program.
kabuky: I couldn't really make much sense of that bit, sorry.
Mark Simpson