且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

游戏编程在C#:精灵碰撞

更新时间:2023-11-23 11:57:40

这是一个有点令人费解。这两个部分:



第一部分(简单&安培;快)



第一部分(涉及v,D1 + D2)可能是令人困惑的,因为,除了使用车间操作的碰撞试验中,图像的尺寸被用于构建边界圈代替。一个简单的碰撞试验,然后进行了使用这些边界圆。这是快速ñ脏'的测试,消除了显然不是碰撞精灵。



两圆圈如果存在的总和(原文如此)的半径大于它们的中心之间的距离重叠因此由毕达哥拉斯我们有碰撞,如果:



(CX1-CX2)2 +(CY1-CY2)2'(R1 + R2)2



查看边界圈这个链接和第二部分。在这个答案的脚下链接



注释代码:

  //获取两个精灵中心
矢量v = this.Position之间的矢量 - s2.Position;

//得到一个圆的半径,将适合的第一个精灵
双D1 =的Math.sqrt(_Image.Width * _Image.Width + _Image.Height * _Image.Height)/ 2;

//得到一个圆的半径,将适合第二个精灵
双D2 =的Math.sqrt(s2._Image.Width * s2._Image.Width + s2._Image.Height * s2._Image.Height)/ 2;

//如果精灵之间的距离大于半径的圆(半径?)放大,它们不碰撞
如果(v.Length> D1 + D2)
返回FALSE;

请注意:您可能需要使用轴向对准包围盒测试,而不是在这里了一圈考虑。如果你有不同的宽度和长度的矩形,这将是一个更有效的/精确的第一次测试。



第二部分(慢)



我没有时间来检查代码100%,但第二部分--having建立两个子画面可能的碰撞,在步骤one--正在执行更复杂的碰撞试验。它正在执行使用精灵的图像源的每个像素碰撞试验 - 特别是其alpha通道。在游戏中,α通道通常被用来存储透明度的表示。该值越大,越不透明的图像(因此0将是100%透明,1.0F将是100%不透明)。



在代码所示,如​​果在这两个精灵像素重叠并且两者都有的> 0.5F,像素共享同一空间Alpha值和代表立体几何,并因此发生碰撞。通过这样的测试,这意味着透明(阅读:隐形)碰撞测试时,精灵的部分将不予考虑,所以你可以有不会在角落等发生碰撞圆形精灵



下面的覆盖这一点更详细一个链接>。


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;
    }
}

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.