XNA'da piksel mükemmel çarpışma algılaması elde etmenin iyi bir yolu var mı?


12

XNA'da piksel mükemmel çarpışma tespiti için iyi bilinen bir yol var mı (veya belki de tekrar kullanılabilir bir kod parçası)?

Bunun ilk geçiş için çokgenler (kutular / üçgenler / daireler) kullanacağını ve çarpışmalar için hızlı test yapacağını ve bu testin bir çarpışmayı göstermesi durumunda piksel başına bir çarpışma arayacağını varsayıyorum.

Bu karmaşık olabilir, çünkü ölçek, rotasyon ve şeffaflığı hesaba katmalıyız.

UYARI: Aşağıdaki yanıttaki bağlantıdan örnek kodu kullanıyorsanız , matrisin ölçeklendirmesinin iyi bir nedenle yorumlandığını unutmayın. Ölçeklendirmenin işe yaraması için bunu kaldırmanıza gerek yoktur.


2
Çokgenler veya spritelar?
doppelgreener

Emin değilim. Xna her ikisini de destekliyor mu? Düzenlemem, her ikisinin bir kombinasyonundan bahsediyor, çünkü sınırlayıcı kutu testleri hızlı bir ilk geçiş.
ashes999

1
Çarpışma algılama, 3D / 2D modelleri veya sprite kullanmanıza bağlı olarak farklılık gösterir. Birinde piksel var. Diğerinin köşeleri ve kenarları vardır.
doppelgreener

Tamam, şimdi ne elde ettiğini görüyorum. Sprite kullanıyorum. Her ne kadar XNA'nın bunları dokulu çokgenler olarak uyguladığına inanıyorum.
ashes999

Yanıtlar:


22

Soruyu 2d olarak etiketlediğinizi görüyorum, bu yüzden devam edip kodumu dökeceğim:

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);
        }

    }

Düzenleme : Bu kod neredeyse kendini açıklayıcı olsa da, yorum yok için kötü hissettim, bu yüzden bazı ekledim;) Ayrıca temelde Color.A özelliği daha karmaşık bir şekilde yaptığı gibi bitwise işlemleri kurtuldum ;)


Yorum veya açıklama içermeyen bir kod dökümü için aşağı oylama.
AttackingHobo

12
Herhangi bir açıklama sadece kodu yeniden yazacaktır ve kod oldukça basittir.

2
Sorumu bundan bahsettiğimi biliyorum, ama tekrar belirtmem gerekiyor: bu ölçekleme ve döndürmeyi ele alıyor mu? İki ölçekli, döndürülmüş sprite doğru bir şekilde kesişebilir mi? (Hızlı bir sınırlama kutusu ilk geçiş ekleyerek olsa başa çıkabilirim.) Yoksa bu çağrıları ile kaplı Boundsmı?
ashes999

1
Evet, unutmuşum! Kod, dönüşümü dikkate almaz. Şu anda düzenleme için zamanım yok, ama ben yapana kadar Çarpışma Dönüşümü örneğine bakabilirsiniz: create.msdn.com/en-US/education/catalog/tutorial/…
pek

1
Sadece bilgiçlikçi olmak, ama ihtiyacın olan: CollidesWith(Sprite other, bool calcPerPixel = true);:)
Jonathan Connell

4

App Hub'da, basit sınırlama kutularından döndürülmüş ve ölçeklendirilmiş spritelar üzerinde 2D çarpışma algılamasından geçen çok eski bir örnek var. Tamamen 4.0 olarak güncellendi. Eğer konu hakkında yeniyseniz, tüm dizi okunmaya değer.

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

Riemer Grootjans'ın yaklaşımını 2D atıcılar oyununda ilginç buldum. http://www.riemers.net/eng/Tutorials/XNA/Csharp/series2d.php

(Ona ulaşmak biraz zaman alır ... http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php ... ama görmek için takip etmek isteyebilirsiniz çözdüğü sorun)

Ancak Riemers örneğinin XNA 4.0 olmadığını ve çalışmasını sağlamak için küçük bir iş yapmanız gerekebileceğini söylüyorum. Ancak zor ya da gizemli bir iş değil.


Büyük bağlantılar, ancak eski bağlantılar; Bunları zaten çözümüm için kullandım.
ashes999

1
Muhteşem. Birisi aradığında sorunuzu bulabileceğini ve daha fazla kaynağa sahip olacağını anlıyorum.
Chris Gomez

0

Siyah beyaz bir çarpışma haritası oluşturmanızı öneririm. Siyah pikseller çarpışma noktaları olacak şekilde programlayın. Karaktere de bir çarpışma haritası verin; Haritaları işlerken, siyah piksellerin büyük karelerini çarpışma dikdörtgenlerine dönüştürecek bir algoritma kullanın. Bu dikdörtgen verileri bir diziye kaydedin. Çarpışmaları aramak için Dikdörtgen kesişme işlevini kullanabilirsiniz. Çarpışma haritasını doku ile de dönüştürebilirsiniz.

Bu bir çarpışma matrisi kullanmaya benzer, ancak daha gelişmiş ve dönüştürebilirsiniz! gerekiyorsa bir çarpışma haritası oluşturma aracı oluşturmayı düşünün. Çalıştırırsanız, lütfen kodu başkalarıyla paylaşın!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.