XNA에서 완벽한 픽셀 충돌 감지 방법이 있습니까?


12

XNA에서 픽셀 완벽 충돌 탐지를위한 잘 알려진 방법 (또는 재사용 가능한 코드 비트)이 있습니까?

필자는 이것이 첫 번째 패스에 폴리곤 (상자 / 삼각형 / 원)을 사용하고 충돌에 대한 빠른 테스트를 사용한다고 가정하고 해당 테스트에서 충돌을 표시하면 픽셀 당 충돌을 검색합니다.

크기, 회전 및 투명도를 고려해야하기 때문에 복잡 할 수 있습니다.

경고 : 아래 답변의 링크에서 샘플 코드를 사용하는 경우 적절한 이유로 매트릭스의 스케일링이 주석 처리됩니다. 스케일링이 작동하도록 주석 처리를 제거 할 필요는 없습니다.


2
다각형 또는 스프라이트?
doppelgreener

잘 모르겠습니다. Xna는 둘 다 지원합니까? 바운딩 박스 테스트가 빠른 첫 번째 통과이기 때문에 편집에서 두 가지의 조합을 언급합니다.
ashes999

1
충돌 감지는 3D / 2D 모델을 사용하는지 스프라이트를 사용하는지에 따라 달라집니다. 하나는 픽셀이 있습니다. 다른 하나에는 꼭짓점과 가장자리가 있습니다.
doppelgreener

좋아, 지금 네가 뭘보고 있는지 스프라이트를 사용하고 있습니다. 비록 XNA가 그것들을 텍스처 폴리곤으로 구현한다고 생각합니다.
ashes999

답변:


22

질문에 2d 태그를 지정 했으므로 코드를 덤프하겠습니다.

class Sprite {

    [...]

    public bool CollidesWith(Sprite other)
    {
        // Default behavior uses per-pixel collision detection
        return CollidesWith(other, true);
    }

    public bool CollidesWith(Sprite other, bool calcPerPixel)
    {
        // Get dimensions of texture
        int widthOther = other.Texture.Width;
        int heightOther = other.Texture.Height;
        int widthMe = Texture.Width;
        int heightMe = Texture.Height;

        if ( calcPerPixel &&                                // if we need per pixel
            (( Math.Min(widthOther, heightOther) > 100) ||  // at least avoid doing it
            ( Math.Min(widthMe, heightMe) > 100)))          // for small sizes (nobody will notice :P)
        {
            return Bounds.Intersects(other.Bounds) // If simple intersection fails, don't even bother with per-pixel
                && PerPixelCollision(this, other);
        }

        return Bounds.Intersects(other.Bounds);
    }

    static bool PerPixelCollision(Sprite a, Sprite b)
    {
        // Get Color data of each Texture
        Color[] bitsA = new Color[a.Texture.Width * a.Texture.Height];
        a.Texture.GetData(bitsA);
        Color[] bitsB = new Color[b.Texture.Width * b.Texture.Height];
        b.Texture.GetData(bitsB);

        // Calculate the intersecting rectangle
        int x1 = Math.Max(a.Bounds.X, b.Bounds.X);
        int x2 = Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width);

        int y1 = Math.Max(a.Bounds.Y, b.Bounds.Y);
        int y2 = Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height);

         // For each single pixel in the intersecting rectangle
         for (int y = y1; y < y2; ++y)
         {
             for (int x = x1; x < x2; ++x)
             {
                 // Get the color from each texture
                 Color a = bitsA[(x - a.Bounds.X) + (y - a.Bounds.Y)*a.Texture.Width];
                 Color b = bitsB[(x - b.Bounds.X) + (y - b.Bounds.Y)*b.Texture.Width];

                 if (a.A != 0 && b.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
                 {
                     return true;
                 }
             }
         }
        // If no collision occurred by now, we're clear.
        return false;
    }

    private Rectangle bounds = Rectangle.Empty;
    public virtual Rectangle Bounds
    {
        get
        {
            return new Rectangle(
                (int)Position.X - Texture.Width,
                (int)Position.Y - Texture.Height,
                Texture.Width,
                Texture.Height);
        }

    }

편집 :이 코드는 거의 자명하지만 주석이 없어서 기분이 좋지 않았습니다.); ;)


주석이나 설명이없는 코드 덤프에 대한 투표.
AttackingHobo

12
모든 설명은 코드를 다시 작성하는 것이며 코드는 매우 간단합니다.

2
나는 내 질문에 이것을 언급했다는 것을 알고 있지만 다시 언급해야합니다. 이것은 스케일링과 회전을 처리합니까? 두 개의 크기 조절 된 회전 스프라이트가 올바르게 교차 할 수 있습니까? (빠른 경계 상자 첫 번째 패스 추가를 처리 할 수 ​​있습니다.) 또는 Bounds?
ashes999

1
네, 잊어 버렸습니다! 코드는 변환을 고려하지 않습니다. 지금은 편집 할 시간이 없지만 다음과 같이 할 때까지 충돌 변환 샘플을 살펴볼 수 있습니다. create.msdn.com/en-US/education/catalog/tutorial/…
pek

1
그냥 pedantic되고 있지만, 당신은 단지 필요 : CollidesWith(Sprite other, bool calcPerPixel = true);:)
Jonathan Connell

4

App Hub에는 간단한 경계 상자에서 회전 및 크기 조정 된 스프라이트의 픽셀 테스트에 이르기까지 2D 충돌 감지를 안내하는 매우 오래된 샘플이 있습니다. 4.0으로 완전히 업데이트되었습니다. 주제를 처음 접한다면 전체 시리즈를 읽을 가치가 있습니다.

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

또한 2D 슈팅 게임에서 Riemer Grootjans 접근 방식이 흥미로 웠습니다. http://www.riemers.net/eng/Tutorials/XNA/Csharp/series2d.php

(그것을 얻는 데 약간의 시간이 걸립니다 ... http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php ... 그가 풀고있는 문제)

그러나 Riemers 샘플은 XNA 4.0이 아니며 실행하기 위해 약간의 작업을 수행해야 할 수도 있습니다. 그러나 어려운 일이 아닙니다.


훌륭한 링크이지만 오래된 링크; 나는 이미 내 솔루션에 사용했습니다.
ashes999

1
대박. 누군가 검색 할 때 질문을 찾을 수 있고 더 많은 리소스를 확보 할 수 있다고 생각합니다.
Chris Gomez

0

흑백 충돌 맵을 만드는 것이 좋습니다. 검은 색 픽셀이 충돌 지점이되도록 프로그래밍하십시오. 캐릭터에게 충돌 맵도 제공하십시오. 맵을 처리 할 때 큰 검은 색 사각형을 충돌 사각형으로 바꾸는 알고리즘을 사용하십시오. 이 사각형 데이터를 배열에 저장하십시오. Rectangle intersects 함수를 사용하여 충돌을 찾을 수 있습니다. 텍스처로 충돌 맵을 변형 할 수도 있습니다.

이것은 충돌 매트릭스를 사용하는 것과 비슷하지만 더 발전하여 변환 할 수 있습니다! 필요한 경우 충돌 맵 생성기 도구를 만들어보십시오. 작동하면 다른 사람들과 코드를 공유하십시오!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.