通常のトップダウン2Dゲームでは、画面のy軸を使用して画像を並べ替えることができます。この例では、ツリーは適切にソートされていますが、等尺性の壁はそうではありません。
壁2は壁1の1ピクセル下にあるため、壁1の後に描画され、最終的に上部に配置されます。
等尺性y軸で並べ替えると、壁は正しい順序で表示されますが、ツリーは表示されません。
これを正しく行うにはどうすればよいですか?
通常のトップダウン2Dゲームでは、画面のy軸を使用して画像を並べ替えることができます。この例では、ツリーは適切にソートされていますが、等尺性の壁はそうではありません。
壁2は壁1の1ピクセル下にあるため、壁1の後に描画され、最終的に上部に配置されます。
等尺性y軸で並べ替えると、壁は正しい順序で表示されますが、ツリーは表示されません。
これを正しく行うにはどうすればよいですか?
回答:
アイソメトリックゲームは機能的に3Dであるため、内部では、ゲーム内の各エンティティの3D座標を保存する必要があります。実際に選択する座標は任意ですが、XとYは地上の2つの軸であり、Zは地上から空中にあるとします。
次に、レンダラーはそれを2Dに投影して画面上に描画する必要があります。「等尺性」はそのような投影法の1つです。3Dから2Dへの等角投影は非常に簡単です。X軸が左上から右下に移動し、2つの水平ピクセルごとに1ピクセル下に移動するとします。同様に、Y軸は右上から左下に移動します。Z軸はまっすぐ上に移動します。3Dから2Dに変換するには、次のようにします。
function projectIso(x, y, z) {
return {
x: x - y,
y: (x / 2) + (y / 2) - z
};
}
さて、元の質問、並べ替えについて。オブジェクトを3Dで直接操作するようになったため、並べ替えがより簡単になりました。ここでの座標空間では、最も遠いスプライトのx、y、z座標が最も低くなります(つまり、3つの軸すべてが画面から向いています)。したがって、それらの合計でソートするだけです。
function nearness(obj) {
return obj.x + obj.y + obj.z;
}
function closer(a, b) {
if (nearness(a) > nearness(b)) {
return "a";
} else {
return "b";
}
}
フレームごとにエンティティを再ソートしないようにするには、ピジョンホールソートを使用します(詳細はこちら)。
あなたのスプライトが長方形のタイルのセットを占めると仮定すると(それらが任意のセットを占める場合、一般的なケースではまったく正しく描画できません)、問題は要素間に完全な順序関係がないため、ソートできないことですO(nlogn)の比較になるソートを使用します。
2つのオブジェクトAとBの場合、AはBの前に描画される(A <-B)か、BはAの前に描画される(B <-A)か、任意の順序で描画できることに注意してください。それらは半順序を形成します。3つのオーバーラップするオブジェクトを使用していくつかの例を描くと、1番目と3番目のオブジェクトがオーバーラップせず、直接依存関係がなくても、それらの間にある2番目のオブジェクトによって描画順序が異なることがわかります。配置すると、異なる描画順序が取得されます。ボトムライン-従来のソートはここでは機能しません。
1つの解決策は、比較(Daniによる言及)を使用し、各オブジェクトを他の各オブジェクトと比較して依存関係を判断し、依存関係グラフ(DAG)を形成することです。次に、グラフでトポロジカルソートを実行して、描画順序を決定します。オブジェクトが多すぎない場合、これは十分に速いかもしれません(そうですO(n^2)
)。
別の解決策は、(平衡化のために- 擬似)クワッドツリーを使用し、すべてのオブジェクトの四角形をそこに格納することです。
次に、すべてのオブジェクトXを反復処理し、クアッドツリーを使用して、オブジェクトXの左端で始まりオブジェクトXの右端で終わるオブジェクトX上のストライプにオブジェクトYがあるかどうかを確認します-このようなすべてのY、Y < -X。このように、グラフを作成し、トポロジー的にソートする必要があります。
しかし、あなたはそれを避けることができます。オブジェクトQのリストとオブジェクトTのテーブルを使用します。X軸(1行)の小さい値から大きい値まですべての表示スロットを反復し、Y軸の行ごとに移動します。そのスロットにオブジェクトの下隅がある場合は、上記の手順を実行して依存関係を判別します。オブジェクトXが部分的にその上にある他のオブジェクトY(Y <-X)に依存し、そのようなすべてのYがすでにQにある場合、XをQに追加します。QにないYがある場合、Xを追加しますTとY <-Xを示します。オブジェクトをQに追加するたびに、Tで保留中のオブジェクトの依存関係を削除します。すべての依存関係が削除されると、TのオブジェクトはQに移動します。
オブジェクトのスプライトは、下、左、または右のスロットからは覗かないと仮定しています(写真のツリーのように、上部のみ)。これにより、多数のオブジェクトのパフォーマンスが向上します。このアプローチは再びになりますがO(n^2)
、奇妙なサイズのオブジェクトやオブジェクトの奇妙な構成を含む最悪の場合のみです。ほとんどの場合、それはO(n * logn * sqrt(n))
です。スプライトの高さを知っていると、sqrt(n)
上のストライプ全体をチェックする必要がないため、を排除できます。画面上のオブジェクトの数に応じて、クアッドツリーを、どのスロットが使用されているかを示す配列に置き換えてみてください(多くのオブジェクトがある場合に意味があります)。
最後に、いくつかのアイデアについてこのソースコードを自由に調べてください:https : //github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala
数学的な解決策はないと思います。おそらく、アイテムが住んでいる2Dの世界には十分なデータがありません。壁がXにミラーリングされている場合、それらは「正しい」順序になります。再度、画像の境界ボックスとのオーバーラップテストを何らかの方法で実行できる場合がありますが、これは私がよく知らない領域です。
おそらく、タイルごとのスクリーンYでソートし、より複雑なものは「設計上の問題」であると言うべきです。たとえば、コンテンツをオーサリングしている場合、デザイナーに並べ替えアルゴリズムを伝え、wall2を2ピクセル突き出させて問題を修正します。それが、私が取り組んだ等尺性ゲームでそれを修正しなければならなかった方法です。これには、「長い」アイテムを取り、タイルサイズのチャンクに分割することが含まれます。
ユーザーがコンテンツを編集することを許可している場合、完全に安全なことは、すべてをタイルベースにし、最大で1つのタイルを大きくすることです。そうすれば、問題を回避できます。あなたはタイルよりもすべてを大きくすることでうまくいくかもしれませんが、おそらく正方形である場合に限ります。私はそれで遊んでいませんでした。
完全な等尺性ソートは困難です。ただし、最初のアプローチでは、アイテムを適切にソートするには、オーバーラップする各オブジェクトでより複雑な比較関数を使用する必要があります。この関数は、次の条件を確認する必要があります。次の場合、重複するオブジェクト「a」は「b」の背後にあります。
(a.posX + a.sizeX <= b.posX)または(a.posY + a.sizeY <= b.posY)または(a.posZ + a.sizeZ <= b.posZ)
もちろん、これは単純な等尺性実装の最初のアイデアです。より複雑なシナリオの場合(ビューの回転、z軸のピクセルごとの位置などが必要な場合)、さらに条件を確認する必要があります。
複数のソリューション、写真、すべての数学を含む広範な回答:
同じx位置でy値を比較すると、毎回機能します。したがって、センターとセンターを比較しないでください。代わりに、1つのスプライトの中心を他のスプライトの同じx位置と比較します。
ただし、これには各スプライトのジオメトリデータが必要です。スプライトの下部境界を表す左から右への2つのポイントと同じくらい簡単です。または、スプライト画像データを分析して、透明ではない最初のピクセルを見つけることができます。
より簡単なアプローチは、すべてのスプライトを3つのグループに分割することです:x軸に沿った対角線、y軸に沿った対角線、およびフラット。2つのオブジェクトが両方とも同じ軸に沿って斜めになっている場合、他の軸に基づいて並べ替えます。
同じタイプのすべてのオブジェクトに一意のIDを割り当てる必要があります。次に、すべてのオブジェクトを位置でソートし、IDの順にオブジェクトのグループを描画します。そのため、グループAのオブジェクト1はグループAのオブジェクト2を上書きしません。
これは実際には答えではありませんが、ここでこのaxel22の答えにコメントと投票をしたいだけです /gamedev//a/8181/112940
私は他の回答に賛成票を投じたりコメントしたりするのに十分な評判はありませんが、彼の回答の2番目の段落は、「モダン」な3Dに頼らずに等尺性ゲームでエンティティを並べ替えようとするときに最も重要なことです。 Zバッファのようなテクニック。
私のエンジンでは、「古い」純粋な2Dをやりたいと思っていました。そして、「ソート」(私の場合はc ++ std :: sort)への呼び出しが特定のマップ構成で正しく機能しなかった理由を解明しようとして、頭を悩ませました。
これが「部分順序」の状況であることに気付いたときのみ、私はそれを解決することができました。
これまでのところ、私がウェブで見つけたすべての実用的なソリューションは、問題を正しく処理するために何らかのトポロジカルソートを使用していました。トポロジカルな並べ替えが方法のようです。