找到两个精灵的碰撞点

时间:2011-05-09 00:42:40

标签: c# xna 2d sprite collision

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

我需要这些信息的原因是这个游戏的本质要求物体具有旋转并根据物理学做一些准确的反应。我目前的测试环境涉及两个方块。有关如何找到联络点的任何建议都会受到欢迎。

此外,如果有人对如何计算在碰撞时移动精灵的距离有任何建议,这样它们就不会重叠,这也很好。

我用于碰撞检测的代码来自:

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

全部谢谢!

1 个答案:

答案 0 :(得分: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);

在spriteBatch.End()

之前添加到Draw()
foreach(Vector2 p in contactPoints)
{
    spriteBatch.Draw(pixelTex, new Rectangle((int)p.X, (int)p.Y, 1, 1), Color.FromNonPremultiplied(120, 255, 100, 255));
}