XNAでピクセル完璧な衝突検出を得る良い方法はありますか?


12

XNAでピクセルパーフェクトコリジョンを検出するためのよく知られた方法(またはおそらく再利用可能なコード)がありますか?

これは、最初のパス、衝突のクイックテストにポリゴン(ボックス/三角形/円)も使用し、そのテストが衝突を示した場合、ピクセルごとの衝突を検索すると想定しています。

スケール、回転、透明度を考慮する必要があるため、これは複雑になる可能性があります。

警告:以下の回答のリンクのサンプルコードを使用している場合、十分な理由でマトリックスのスケーリングがコメント化されていることに注意してください。スケーリングを機能させるためにコメントを外す必要はありません。


2
ポリゴンまたはスプライト?
-doppelgreener

よく分かりません。Xnaは両方をサポートしていますか?私の編集では、両方の組み合わせに言及しています。なぜなら、バウンディングボックステストは高速な最初のテストだからです。
ashes999

1
衝突検出は、3D / 2Dモデルを使用しているかスプライトを使用しているかによって異なります。1つにはピクセルがあります。もう一方には頂点とエッジがあります。
-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プロパティがより複雑な方法で行うことを基本的に行っていたため、ビットごとの操作も削除しました;)


コメントや説明のないコードダンプのダウン投票。
AttackingHobo

12
どんな説明でもコードを修正するだけで、コードはかなり簡単です。

2
私は私の質問でこれを言及したことを知っていますが、私はそれをもう一度述べる必要があります:これはスケーリングと回転を処理しますか?2つの拡大縮小され回転されたスプライトは正しく交差できますか?(ただし、クイックバウンディングボックスの最初のパスの追加を処理できます。)または、これはへの呼び出しでカバーされていますBoundsか?
ashes999

1
はい、私はそれを忘れました!コードは変換を考慮していません。今、私が編集に時間を持っていないが、私はやるまで、あなたが衝突する形質転換サンプルを見てすることができますcreate.msdn.com/en-US/education/catalog/tutorial/...
PEK

1
ただつまらないものですが、必要なのは:CollidesWith(Sprite other, bool calcPerPixel = true);:)
ジョナサンコネル

4

App 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
驚くばかり。誰かがあなたの質問を見つけることができ、より多くのリソースを見つけることができるとき、私はちょうど考えます。
クリス・ゴメス

0

白黒の衝突マップを作成することをお勧めします。黒いピクセルが衝突点になるようにプログラムします。キャラクターにもコリジョンマップを与えます。マップを処理するときは、黒いピクセルの大きな正方形を衝突長方形に変換するアルゴリズムを使用します。この長方形データを配列に保存します。四角形の交差機能を使用して、衝突を探すこ​​とができます。テクスチャを使用して衝突マップを変換することもできます。

これは衝突行列を使用するのとよく似ていますが、より高度であり、変換することができます!必要に応じて、コリジョンマップジェネレーターツールの構築を検討してください。動作させる場合は、コードを他の人と共有してください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.