2D ゲームで等尺性タイルを描画する正しい方法は何ですか?
マップの 2D 配列表現の各列をジグザグにする方法でタイルをレンダリングすることを示唆する参考文献 (このようなもの)を読みました。画面に描画されるものが、2D 配列がどのように見えるかにより密接に関連し、少し回転するだけで、ダイヤモンドのように描画する必要があると思います。
どちらの方法にも利点または欠点はありますか?
更新: マップ レンダリング アルゴリズムの修正、イラストの追加、書式の変更。
おそらく、タイルを画面にマッピングするための「ジグザグ」手法の利点は、タイルx
とy
座標が垂直軸と水平軸上にあると言えます。
「ダイヤモンドで描く」アプローチ:
「ひし形での描画」を使用してアイソメ マップを描画することにより、for
これは次の例のように、2 次元配列に対してネストされたループを使用してマップをレンダリングすることを意味すると思います。
tile_map[][] = [[...],...]
for (cellY = 0; cellY < tile_map.size; cellY++):
for (cellX = 0; cellX < tile_map[cellY].size cellX++):
draw(
tile_map[cellX][cellY],
screenX = (cellX * tile_width / 2) + (cellY * tile_width / 2)
screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
)
アドバンテージ:
このアプローチの利点は、for
すべてのタイルで一貫して機能する単純なネストされたループであることです。
不利益:
このアプローチの欠点の 1 つは、マップ上のタイルx
のy
座標が対角線で増加することです。これにより、画面上の位置を配列として表されたマップに視覚的にマッピングすることが難しくなる可能性があります。
ただし、上記のサンプル コードの実装には落とし穴があります。レンダリングの順序により、特定のタイルの後ろにあるはずのタイルが、前のタイルの上に描画されます。
この問題を修正するには、内側for
の -loop の順序を逆にする必要があります。つまり、最も高い値から始めて、低い値に向かってレンダリングします。
tile_map[][] = [[...],...]
for (i = 0; i < tile_map.size; i++):
for (j = tile_map[i].size; j >= 0; j--): // Changed loop condition here.
draw(
tile_map[i][j],
x = (j * tile_width / 2) + (i * tile_width / 2)
y = (i * tile_height / 2) - (j * tile_height / 2)
)
上記の修正により、マップのレンダリングが修正されます。
「ジグザグ」アプローチ:
アドバンテージ:
おそらく、「ジグザグ」アプローチの利点は、レンダリングされたマップが「ダイアモンド」アプローチよりも垂直方向に少しコンパクトに見えることです。
不利益:
for
ジグザグ手法を実装しようとすると、配列内の各要素に対するネストされたループのように単純に記述できないため、レンダリング コードを記述するのが少し難しくなるという欠点があります。
tile_map[][] = [[...],...]
for (i = 0; i < tile_map.size; i++):
if i is odd:
offset_x = tile_width / 2
else:
offset_x = 0
for (j = 0; j < tile_map[i].size; j++):
draw(
tile_map[i][j],
x = (j * tile_width) + offset_x,
y = i * tile_height / 2
)
また、レンダリング順序がずれているため、タイルの座標を把握するのが少し難しい場合があります。
注: この回答に含まれる図は、提示されたタイル レンダリング コードの Java 実装で作成され、次のint
配列がマップとして使用されます。
tileMap = new int[][] {
{0, 1, 2, 3},
{3, 2, 1, 0},
{0, 0, 1, 1},
{2, 2, 3, 3}
};
タイル画像は次のとおりです。
tileImage[0] ->
中に箱が入った箱。tileImage[1] ->
ブラックボックス。tileImage[2] ->
白い箱。tileImage[3] ->
背の高い灰色の物体が入った箱。タイルの幅と高さに関する注意
上記のコード例で使用されている変数tile_width
およびtile_height
which は、タイルを表す画像内の地面タイルの幅と高さを参照しています。
画像の寸法とタイルの寸法が一致する限り、画像の寸法を使用しても問題ありません。そうしないと、タイル マップがタイル間にギャップを持ってレンダリングされる可能性があります。
どちらの方法でも、仕事は完了します。ジグザグとは、次のような意味だと思います:(数字はレンダリングの順序です)
.. .. 01 .. ..
.. 06 02 ..
.. 11 07 03 ..
16 12 08 04
21 17 13 09 05
22 18 14 10
.. 23 19 15 ..
.. 24 20 ..
.. .. 25 .. ..
ダイヤモンドとは、次のことを意味します。
.. .. .. .. ..
01 02 03 04
.. 05 06 07 ..
08 09 10 11
.. 12 13 14 ..
15 16 17 18
.. 19 20 21 ..
22 23 24 25
.. .. .. .. ..
最初の方法では、画面全体が描画されるようにレンダリングするタイルを増やす必要がありますが、簡単に境界チェックを行い、画面外のタイルを完全にスキップできます。どちらの方法も、タイル 01 の位置を特定するために、ある程度の数を計算する必要があります。最終的に、どちらの方法も、特定のレベルの効率に必要な計算に関してはほぼ同じです。
ダイヤモンドの境界を超えるタイルがある場合は、深さ順に描画することをお勧めします。
...1...
..234..
.56789.
..abc..
...d...
最も高く、視聴者に最も近いポイントからのユークリッド距離を使用できますが、それはまったく正しくありません。球状のソート順になります。遠くから見ることで、それを正すことができます。さらに離れると、曲率は平らになります。したがって、x、y、z の各コンポーネントに 1000 を足すだけで、x'、y'、z' が得られます。x'*x'+y'*y'+z'*z' の並べ替え。
本当の問題は、他の 2 つ以上のタイルと交差する/またがるタイル/スプライトを描画する必要がある場合です。
2 か月間の個人的な問題分析の後、ついに新しい cocos2d-js ゲームの「正しいレンダリング描画」を見つけて実装しました。解決策は、スプライトが「前、後、上、後ろ」である各タイル(影響を受けやすい)のマッピングにあります。それができたら、「再帰ロジック」に従ってそれらを描画できます。