2011-05-09 107 views
1

我正在C#和XNA的2D游戏中工作,我已经成功地获得了使用精灵工作的每像素碰撞检测。我现在困惑的挑战是如何计算两个精灵发生碰撞的位置。查找两个精灵的碰撞点

我之所以需要这个信息是这个游戏的本质要求对象具有旋转和反应有点准确地根据物理学。我目前的测试环境涉及两个方块。有关如何找到联系点的任何建议都会受到欢迎。

此外,如果任何人有关于如何计算移动的距离碰撞的精灵,使他们不重叠,这将是巨大的任何建议。

我使用碰撞检测代码来自这里:

http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed

谢谢大家!

回答

3

有关获取的接触点,你链接到已经完成了艰难的工作代码,而不是返回返回true通过变换回世界空间坐标的第一部分。

下面有从http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel_transformed

public static IEnumerable<Vector2> IntersectPixels(
        Matrix transformA, int widthA, int heightA, Color[] dataA, 
        Matrix transformB, int widthB, int heightB, Color[] dataB) 
{ 
    // Calculate a matrix which transforms from A's local space into 
    // world space and then into B's local space 
    Matrix transformAToB = transformA * Matrix.Invert(transformB); 

    // When a point moves in A's local space, it moves in B's local space with a 
    // fixed direction and distance proportional to the movement in A. 
    // This algorithm steps through A one pixel at a time along A's X and Y axes 
    // Calculate the analogous steps in B: 
    Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB); 
    Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB); 

    // Calculate the top left corner of A in B's local space 
    // This variable will be reused to keep track of the start of each row 
    Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB); 

    // For each row of pixels in A 
    for(int yA = 0; yA < heightA; yA++) 
    { 
     // Start at the beginning of the row 
     Vector2 posInB = yPosInB; 

     // For each pixel in this row 
     for(int xA = 0; xA < widthA; xA++) 
     { 
      // Round to the nearest pixel 
      int xB = (int)Math.Round(posInB.X); 
      int yB = (int)Math.Round(posInB.Y); 

      // If the pixel lies within the bounds of B 
      if(0 <= xB && xB < widthB && 
       0 <= yB && yB < heightB) 
      { 
       // Get the colors of the overlapping pixels 
       Color colorA = dataA[xA + yA * widthA]; 
       Color colorB = dataB[xB + yB * widthB]; 

       // If both pixels are not completely transparent, 
       if(colorA.A != 0 && colorB.A != 0) 
       { 
        // then an intersection has been found 
        yield return Vector2.Transform(new Vector2(xA, yA),transformA); 
       } 
      } 

      // Move to the next pixel in the row 
      posInB += stepX; 
     } 

     // Move to the next row 
     yPosInB += stepY; 
    } 

    // No intersection found 
} 

稍微修改后的代码作为所述第二部分的常用方法是添加一个小的力相反的方向碰撞排斥它们。关于游戏物理的这个article是一个很好的入门书,并且很少有像Farseer这样强大的现成物理引擎。

样本代码是用以转换精灵,如果你不需要此功能,您也许可以简化代码。如果您在移动物理引擎时不使用物理引擎来避免重叠,则可能需要移动另一个物理引擎等,物理引擎会为您处理此问题。

编辑: 下面是一些小的变化到MSDN样本所以每个接触点绘制一个绿色像素。

添加这些字段

//Contact points are cleared and re-added each update 
List<Vector2> contactPoints = new List<Vector2>(); 
//Texture for contact display 
Texture2D pixelTex; 

添加到LoadContent()地方

pixelTex = new Texture2D(GraphicsDevice, 1, 1); 
pixelTex.SetData<Color>(new[] { Color.White }); 

更换的Update()结束本

// Update each block 
personHit = false; 
contactPoints.Clear(); 
for(int i = 0; i < blocks.Count; i++) 
{ 
    // Animate this block falling 
    blocks[i].Position += new Vector2(0.0f, BlockFallSpeed); 
    blocks[i].Rotation += BlockRotateSpeed; 

    // Build the block's transform 
    Matrix blockTransform = 
     Matrix.CreateTranslation(new Vector3(-blockOrigin, 0.0f)) * 
     // Matrix.CreateScale(block.Scale) * would go here 
     Matrix.CreateRotationZ(blocks[i].Rotation) * 
     Matrix.CreateTranslation(new Vector3(blocks[i].Position, 0.0f)); 

    // Calculate the bounding rectangle of this block in world space 
    Rectangle blockRectangle = CalculateBoundingRectangle(
       new Rectangle(0, 0, blockTexture.Width, blockTexture.Height), 
       blockTransform); 

    // The per-pixel check is expensive, so check the bounding rectangles 
    // first to prevent testing pixels when collisions are impossible. 
    if(personRectangle.Intersects(blockRectangle)) 
    { 
     contactPoints.AddRange(IntersectPixels(personTransform, personTexture.Width, 
          personTexture.Height, personTextureData, 
          blockTransform, blockTexture.Width, 
          blockTexture.Height, blockTextureData)); 
     // Check collision with person 
     if(contactPoints.Count != 0) 
     { 
      personHit = true; 
     } 
    } 

    // Remove this block if it have fallen off the screen 
    if(blocks[i].Position.Y > 
     Window.ClientBounds.Height + blockOrigin.Length()) 
    { 
     blocks.RemoveAt(i); 

     // When removing a block, the next block will have the same index 
     // as the current block. Decrement i to prevent skipping a block. 
     i--; 
    } 
} 

base.Update(gameTime); 

添加到Draw() spriteBatch.End()之前

foreach(Vector2 p in contactPoints) 
{ 
    spriteBatch.Draw(pixelTex, new Rectangle((int)p.X, (int)p.Y, 1, 1), Color.FromNonPremultiplied(120, 255, 100, 255)); 
} 
+0

谢谢您的回复,但我有困难的时候将这种变化。首先,我不熟悉“收益率”以及为什么会在这种情况下使用它。 (已查找它,但在MSDN上,但我很难理解它)此外,在这种情况下返回什么,以及我将如何处理它。在我调用函数作为if语句的“参数”之前。但我不认为这将在这种情况下工作。 – APalmer 2011-05-11 04:40:23

+0

我使用了收益率,因为可能有多个联系点。结果是IEnumerable,所以你可以在结果上使用'foreach'或者使用Linq的ToArray,ToList扩展方法。您可以将点添加到列表中并返回,但是根据需要使用yield获取,所以使用Linq可以使用'IntersectPixels(...)。Any()'作为返回bool的原始方法的替代品。检查编辑的例子。 – Kris 2011-05-11 18:19:19

+0

先生,你是我的英雄。我相信我欠你一个火鸡三明治。 – APalmer 2011-05-14 23:09:26