มีวิธีที่ดีในการตรวจจับการชนกันของพิกเซลใน 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);
        }

    }

แก้ไข : ในขณะที่รหัสนี้เกือบจะอธิบายตัวเองฉันรู้สึกไม่ดีที่ไม่ได้มีความคิดเห็นดังนั้นฉันจึงเพิ่มบางส่วน;) ฉันยังได้กำจัดการดำเนินการระดับบิตเนื่องจากมันทำโดยทั่วไปสิ่งที่คุณสมบัติ Color.A ทำในลักษณะที่ซับซ้อนมากขึ้น ;)


ลงคะแนนสำหรับการถ่ายโอนข้อมูลรหัสที่ไม่มีความคิดเห็นหรือคำอธิบาย
AttackHobo

12
คำอธิบายใด ๆ ที่เพิ่งจะเรียกคืนรหัสและรหัสตรงไปตรงมาค่อนข้าง

2
ฉันรู้ว่าฉันพูดถึงสิ่งนี้ในคำถามของฉัน แต่ฉันจำเป็นต้องระบุอีกครั้ง: สิ่งนี้จัดการการปรับขนาดและการหมุนหรือไม่? สไปรต์ที่ปรับสเกลสองสเกลสามารถตัดกันอย่างถูกต้องได้หรือไม่? (ฉันสามารถจัดการกับการเพิ่มการข้ามผ่านกล่องแรกได้อย่างรวดเร็ว) หรือสิ่งนี้ถูกคลุมด้วยการโทรถึงBoundsหรือไม่
ashes999

1
ใช่ฉันลืมไปแล้ว! รหัสไม่ได้คำนึงถึงการแปลง ตอนนี้ฉันไม่มีเวลาแก้ไข แต่คุณสามารถดูตัวอย่างการชนกันของข้อมูลได้จนกว่าฉันจะทำ: create.msdn.com/en-US/education/catalog/tutorial/ …
pek

1
เพียงแค่อวดรู้ แต่คุณต้องการ: CollidesWith(Sprite other, bool calcPerPixel = true);:)
Jonathan Connell

4

บนแอป Hub มีตัวอย่างที่เก่าแก่มากที่นำคุณไปสู่การตรวจจับการชนกันของภาพ 2D จากกล่องขอบเขตอย่างง่ายไปจนถึงการทดสอบพิกเซลบนสไปรต์ที่หมุนและปรับขนาด มันได้รับการอัพเดตอย่างสมบูรณ์เป็น 4.0 ทั้งชุดมีค่าอ่านถ้าคุณยังใหม่กับหัวข้อ

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

ฉันพบว่า Riemer Grootjans เข้าใกล้เกมยิง 2D ของเขา 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.