Có cách nào tốt để có được phát hiện va chạm hoàn hảo pixel trong XNA không?


12

Có cách nào nổi tiếng (hoặc có thể sử dụng lại đoạn mã) để phát hiện va chạm hoàn hảo pixel trong XNA không?

Tôi giả sử điều này cũng sẽ sử dụng các đa giác (hộp / hình tam giác / vòng tròn) cho lần thử đầu tiên, kiểm tra nhanh các va chạm và nếu thử nghiệm đó chỉ ra một vụ va chạm, thì nó sẽ tìm kiếm xung đột trên mỗi pixel.

Điều này có thể phức tạp, bởi vì chúng ta phải tính đến quy mô, luân chuyển và tính minh bạch.

CẢNH BÁO: Nếu bạn đang sử dụng mã mẫu từ liên kết từ câu trả lời bên dưới, hãy lưu ý rằng tỷ lệ của ma trận được nhận xét vì lý do chính đáng. Bạn không cần phải bỏ qua nó để mở rộng quy mô để làm việc.


2
Đa giác hay sprite?
doppelgreener

Tôi không chắc. Xna hỗ trợ cả hai? Chỉnh sửa của tôi đề cập đến sự kết hợp của cả hai, vì các bài kiểm tra giới hạn là một bước đầu tiên nhanh chóng.
tro999

1
Việc phát hiện va chạm sẽ khác nhau tùy thuộc vào việc bạn đang sử dụng mô hình 3D / 2D hay các họa tiết. Một cái có pixel. Cái kia có đỉnh và cạnh.
doppelgreener

Được rồi, tôi thấy những gì bạn nhận được bây giờ. Tôi đang sử dụng sprite. Mặc dù tôi tin rằng XNA thực hiện chúng như một đa giác có kết cấu.
tro999

Câu trả lời:


22

Tôi thấy rằng bạn đã gắn thẻ câu hỏi là 2d, vì vậy tôi sẽ tiếp tục và chuyển mã của mình:

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

    }

Chỉnh sửa : Mặc dù mã này gần như tự giải thích, tôi cảm thấy tồi tệ vì không có bình luận, vì vậy tôi đã thêm một số;) Tôi cũng đã loại bỏ các hoạt động bitwise vì nó thực hiện cơ bản những gì thuộc tính Color.A làm theo cách phức tạp hơn ;)


Bỏ phiếu cho một bãi chứa mã mà không có ý kiến ​​hoặc giải thích.
Tấn

12
Bất kỳ lời giải thích nào sẽ chỉ là khôi phục mã, và mã này khá đơn giản.

2
Tôi biết tôi đã đề cập đến vấn đề này trong câu hỏi của mình, nhưng tôi cần nói lại một lần nữa: điều này có xử lý tỷ lệ và xoay không? Hai sprite xoay, xoay có thể giao nhau chính xác? (Tôi có thể xử lý việc thêm một hộp giới hạn nhanh đầu tiên mặc dù.) Hoặc điều này có được bao phủ với các cuộc gọi đến Boundskhông?
tro999

1
Vâng, tôi quên mất điều đó! Các mã không đưa vào chuyển đổi vào xem xét. Ngay bây giờ tôi không có thời gian để chỉnh sửa, nhưng bạn có thể xem mẫu Collision Transformed cho đến khi tôi thực hiện: created.msdn.com/en-US/education/catalog/tutorial/iêu
pek

1
Chỉ là mô phạm, nhưng bạn chỉ cần: CollidesWith(Sprite other, bool calcPerPixel = true);:)
Jonathan Connell

4

Trên Trung tâm ứng dụng, có một mẫu rất cũ hướng dẫn bạn phát hiện va chạm 2D từ các hộp giới hạn đơn giản đến thử nghiệm pixel trên các họa tiết xoay và thu nhỏ. Nó đã được cập nhật đầy đủ lên 4.0. Toàn bộ loạt bài đáng để đọc nếu bạn chưa quen với chủ đề này.

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

Tôi cũng tìm thấy cách tiếp cận Riemer Grootjans thú vị trong trò chơi bắn súng 2D của mình. http://www.riemers.net/eng/Tutorials/XNA/Csharp/series2d.php

(Phải mất một chút thời gian để truy cập nó ... http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php ... nhưng bạn có thể muốn theo dõi để xem vấn đề anh ấy giải quyết)

Nhưng tôi cảnh báo bạn rằng mẫu Riemers không phải là XNA 4.0 và bạn có thể phải làm một công việc nhỏ để chạy nó. Tuy nhiên, đó không phải là công việc khó khăn hay bí ẩn.


Liên kết tuyệt vời, nhưng liên kết cũ; Tôi đã sử dụng chúng cho giải pháp của tôi.
tro999

1
Tuyệt vời. Tôi chỉ hình dung khi ai đó tìm kiếm họ có thể tìm thấy câu hỏi của bạn và họ sẽ có nhiều tài nguyên hơn.
Chris Gomez

0

Tôi khuyên bạn nên tạo một bản đồ va chạm đen trắng. Lập trình nó để các pixel đen là điểm va chạm. Cung cấp cho nhân vật một bản đồ va chạm quá; Khi xử lý bản đồ, sử dụng thuật toán sẽ biến các ô vuông lớn màu đen thành hình chữ nhật va chạm. Lưu dữ liệu hình chữ nhật này trong một mảng. Bạn có thể sử dụng chức năng Giao diện hình chữ nhật để tìm kiếm va chạm. Bạn cũng có thể biến đổi bản đồ va chạm với kết cấu.

Điều này rất giống với việc sử dụng ma trận va chạm nhưng cao cấp hơn và bạn có thể biến đổi nó! xem xét xây dựng một công cụ tạo bản đồ va chạm nếu bạn cần nó. Nếu bạn làm cho nó hoạt động, xin vui lòng chia sẻ mã với những người khác!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.