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.