回答:
私はAndroidに慣れていないので、あなたが自由に使えるツールはわかりませんが、これを一般的な用語で実装する方法を説明できます。それがどれほど簡単になるかは、Androidが提供する機能によって異なります。マトリックスが必要になるか、少なくとも計算が大幅に簡略化されます。
まず、境界ボックスの衝突チェックを行い、衝突しない場合は、それ以上の計算を回避するためにすぐに戻ります。境界ボックスが衝突しない場合、衝突するピクセルもないことが保証されているため、これは論理的です。
その後、ピクセルパーフェクトコリジョンチェックが必要な場合、最も重要なポイントは、そのチェックを同じ空間で実行する必要があることです。これは、スプライトAから各ピクセルを取得し、一連の変換を適用してそれらをスプライトBのローカル空間に取り込み、スプライトB上のその位置にあるピクセルと衝突するかどうかを確認することで実行できます。チェックは不透明です。
したがって、最初に必要なのは、各スプライトのワールドマトリックスを作成することです。おそらく、オンラインでチュートリアルを作成する方法を教えるチュートリアルがありますが、基本的には、いくつかの単純なマトリックスを次の順序で連結したものでなければなりません。
Translation(-Origin) * Scale * Rotation * Translation(Position)
このマトリックスの有用性は、ローカルスペース内のポイントを乗算することで(たとえばbitmap.getPixelAt(10,20)
、10,20のようなメソッドを使用してピクセルをローカルスペースで定義した場合)、対応するワールドマトリックスによって、ワールドスペースに移動します。
LocalA * WorldMatrixA -> World
LocalB * WorldMatrixB -> World
また、マトリックスを反転すると、逆方向に移動することもできます。つまり、使用したマトリックスに応じて、ポイントをワールドスペースから各スプライトのローカルスペースに変換します。
World * InverseWorldMatrixA -> LocalA
World * InverseWorldMatrixB -> LocalB
したがって、スプライトAのローカルスペースからスプライトBのローカルスペースにポイントを移動するには、まずスプライトAのワールドマトリックスを使用してポイントを変換し、それをワールドスペースに取得してから、スプライトBの逆ワールドマトリックスを使用してそれを取得します。スプライトBのローカル空間:
LocalA * WorldMatrixA -> World * InverseWorldMatrixB -> LocalB
変換後、新しいポイントがスプライトBの境界内にあるかどうかを確認し、そうである場合は、スプライトAの場合と同じようにその位置のピクセルを確認します。したがって、プロセス全体は次のようになります(疑似コードおよび未テスト)。 :
bool PixelCollision(Sprite a, Sprite B)
{
// Go over each pixel in A
for(i=0; i<a.Width; ++i)
{
for(j=0; j<a.Height; ++j)
{
// Check if pixel is solid in sprite A
bool solidA = a.getPixelAt(i,j).Alpha > 0;
// Calculate where that pixel lies within sprite B's bounds
Vector3 positionB = new Vector3(i,j,0) * a.WorldMatrix * b.InverseWorldMatrix;
// If it's outside bounds skip to the next pixel
if(positionB.X<0 || positionB.Y<0 ||
positionB.X>=b.Width || positionB.Y>=b.Height) continue;
// Check if pixel is solid in sprite B
bool solidB = b.getPixelAt(positionB.X, positionB.Y).Alpha > 0;
// If both are solid then report collision
if(solidA && solidB) return true;
}
}
return false;
}
David Gouveiaの答えは正しいように聞こえますが、パフォーマンスの観点からは、これは最良のソリューションではありません。実行する必要があるいくつかの重要な最適化があります。
単純な円の衝突で最初にチェックすることにより、不要なチェックをソートして回避します。任意の回転でスプライトの中心と半径を取得するには、4つすべての(すでに回転した)頂点の最小および最大のx座標とy座標を取得します。スプライトで閉じた円:
center = max_x-min_x / 2、max_y-min_y / 2
半径= max(max_x-min_x、max_y-min_y)
これで、基本的に単純なアフィンテクスチャマッピングアルゴリズムを使用して、既に変換された(回転された)画像をラスタライズしてチェックする候補が多すぎないはずです。基本的には、ラスタライズされたスプライトごとに4行を追跡します。頂点aから回転ボックスの次の頂点(bおよびc)に向かう2行頂点u1 / v1から次の頂点u2 / v2に「ビットマップ空間」に進む2行およびu3 / v3:注:この画像をググると、三角形が表示されます。ボックスは2つの三角形にすぎません。このアルゴリズムでは、丸めエラーによるビットマップ内の「穴」を回避するために、水平線を描画することが重要です(これが「ラスタライザ」と呼ばれる理由です)。bresenhamアルゴリズムでラインを計算する、各ピクセルに必要なのは4つの追加と4つの比較(および傾斜によっては2つの追加の追加)だけです。ただし、独自のポリゴンテクスチャマッパーを作成しますが、コストのかかる(最適化が難しい)3D補正は必要ありません。
衝突ビットマップの解像度を簡単に下げ(たとえば、ファクター2で)、さらに時間を節約できます。解像度の半分の衝突チェックは気付かれません。
グラフィックアクセラレーションを使用している場合、ラスタライザを自分でコーディングすることを回避するために、ある種のHW加速バッファチェック(ステンシル?)を使用できる可能性があります。
主な問題は次のとおりです。Javaは、2D配列に格納されているビットマップデータへのアクセスが高速ではありません。アクセスごとに少なくとも1つのindexOutOfBoundsチェックを回避するために、データを1次元配列に格納することをお勧めします。また、2の累乗の次元(64x64、128x128など)を使用します。これにより、乗算ではなくビットシフトによってオフセットを計算できます。最初のテクスチャに値!= 0(透明)がある場合にのみ、2番目のテクスチャアクセスを最適化して実行することもできます。
これらの問題はすべてソフトウェアレンダリングエンジンで解決されました。ソースコードを調べると役立つ場合があります