2011-05-09 111 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