17

等角グリッド

私は座標がグリッドの左隅(上の画像に示されている隅)の[0,0]から始まり、xが画像の下に向かって増加し、yが上に向かって増加する等角グリッドシステムを持っています(したがって[ 0, 高さ] が上隅、[幅, 0] が下隅になり、幅と高さがグリッドのサイズ (つまり 200 x 200 正方形) のひし形になります。

とにかく、私が助けを必要としているのは、画像に示されている青いボックス内に含まれるアイソメトリック グリッド位置の配列を取得することです。すべての x、y スクリーン位置を反復処理して、対応するグリッド位置を取得することはできません (スクリーン位置からグリッド位置に変換する方法について、以前に提起したこの質問を参照してください。アイソメトリック グリッドで行/列を取得します。 )方法がわからないこれを効率的に達成するために。

私が以前に見つけた質問がありましたが、これはここのリンクとほぼ同じです。答えは、グリッドの正方形ごとに異なる色の画像にグリッドをレンダリングし、正方形の下に存在する色を検出することでした。私はこのソリューションを実装しましたが、非常に遅いです! 選択ボックス内の各ピクセルのグリッド位置を確認する方が速いとほとんど考えています。なぜああ、なぜ JavaScript のループがこんなに遅いのか!

私は自分の座標系に基づいてこの問題の数学的解決策を本当に必要としていますが、機能するものを思い付くことができないようです(グリッドから外れた選択ボックスも処理します)

さらに情報が必要な場合はお知らせください。

編集:残念ながら、提供された回答はこれまでのところ機能していません。選択は、正方形のグリッド上でダイヤモンドが選択された領域を持つようなものであるため、回答のポイントを逃さない限り、反復する左上隅、右下隅は実際にはありませんか? レンダリング アプローチを最適化しましたが、大きな選択範囲では、すべてのピクセル チェック カラーをループして対応する正方形を取得するため、フレームの顕著な低下が追加されます。

4

6 に答える 6

10

線形代数が答えです。ここでは、画面座標と等角座標の 2 つの座標系に注目します。選択した領域の角を画面座標から等角座標に変換すると、非常に役立ちます。

画面上で測定されたアイソメトリック座標の x 軸と y 軸の間の角度をシータとし、アイソメトリック座標の 1 ステップのピクセル長を単位とします。それで

var c = Math.cos(theta/2);
var s = Math.sin(theta/2);
var origin = [oX, oY]; //the pixel coordinates of (0, 0)
var unit = 20;
var iso2Screen = function(iso) {
  var screenX = origin[0] + unit * (iso[0] * c + iso[1] * c);
  var screenY = origin[1] + unit * (iso[0] * -s + iso[1] * s);
  return [screenX, screenY];
}

この関係を逆にすると、

var screen2Iso = function(screen) {
  var isoX = ((screen[0] - origin[0]) / c - (screen[1] - origin[1]) / s) / unit;
  var isoY = ((screen[0] - origin[0]) / c + (screen[1] - origin[1]) / s) / unit;

次に、選択ボックスの各コーナーの画面座標を等角座標に変換し、最小と最大の x と y を取得します。

var cornersScreen = ...//4-element array of 2-element arrays
var cornersIso = [];
for(var i = 0; i < 4; i++) {
  cornersIso.push(screen2Iso(cornersScreen[i]));
}
var minX, maxX, minY, maxY;
minX = Math.min(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
maxX = Math.max(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
minY = Math.min(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);
maxY = Math.max(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);

選択したアイソメトリック ポイントはすべてアイソメトリック ボックス [minX, maxX] x [minY, maxY] 内にありますが、そのボックス内のすべてのポイントが選択範囲内にあるわけではありません。

選択範囲に含まれていないボックス内のポイントを取り除くために、さまざまなことができます。等角座標の x と y の整数値を反復処理し、等角座標を画面座標に変換してから、その画面座標が選択ボックス内にあるかどうかをテストすることをお勧めします。

var sMinX, sMaxX, sMinY, sMaxY;
sMinX = Math.min(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMaxX = Math.max(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMinY = Math.min(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
sMaxY = Math.max(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
var selectedPoints = [];
for(var x = Math.floor(minX); x <= Math.ceil(maxX); x++) {
  for(var y = Math.floor(minY); x <= Math.ceil(maxY); y++) {
    var iso = [x,y];
    var screen = iso2Screen(iso);
    if(screen[0] >= sMinX && screen[0] <= sMaxX && screen[1] >= sMinY && screen[1] <= sMaxY) {
      selectedPoints.push(iso);
    }
  }
}
于 2011-08-19T16:40:58.453 に答える
7

私は趣味でXNAゲームをしている学生です。私は正方形に基づく古典的な2Dゲームに取り組んでいます(Project Squared)。このあなたのゲームは私の仕事を思い出させてくれたので、私はあなたを助けることにしました。

あなたが言ったようにグラフィックスではなく、数学を使ってそれを行うべきだと思います。

まず、どの正方形(グリッド上の位置)が「青いボックス」(おそらく選択ボックス)の開始と選択の終了にあるかを知る必要があります。これで、リストの始まりと終わりがわかりました。

ゲームの正方形はおそらく静的なサイズであるため(またはカメラをズームしますが、幅/高さを増やすことはありません)、選択ボックスにある正方形を簡単に計算できます。

これで、正方形が45°r/l移動したことがわかります。

(私はcoordsysのようなXNAについて話している-左上0,0)

If ( selectedStartSquare.selectionY <= square.height )
    startY = selectedStartSquare.selectionY
else startY = selectedStartSquare.selectionY + 1; 

if (selectedEndSquare.selectionY > square.height)
    endY = -||-.selectionY
else endY = -||- + 1;

これにより、正方形の開始と終了の高さのインデックスが得られます。Xインデックスに対して行う必要があるのと同じこと。

これで、すべての正方形を取得するために必要なものがすべて揃いました。XをselectedStartSquare.XからendXに移動し、forループでselectedStartSquare.YからendYにトラフしますが、開始は毎回変更します(startYIndex ++ループごと)

私はアイソメトリックゲームを扱ったことがないので、これは単なる良い例です。これにはおそらく多少の調整が必要ですが、私は「考えます!!」これはうまくいくはずです。(私はすでにベッドにいたので、寝る前に紙も使わずにこれを書きました...頑張ってください)

英語でごめんなさい、私はクロアチアから来たので...

于 2011-08-10T21:42:41.753 に答える
4

アレクサンダーはあなたの問題を解決する方法について非常に良い考えを持っていると思います.

代替案は次のとおりです。

チェックするグリッド スクエアの数を減らす簡単な方法は、グリッド座標で青いボックスの角の座標を見つけることです。あなたの例では、四角形をチェックするだけで済み1<x<13 and 3<y<16ます。

alvaro は、ポイントがボックス内にあるかどうかを確認する方法について、短く簡潔な回答を提供します。

于 2011-08-10T21:50:26.720 に答える
4

タイルの通常のレイアウトを考慮すると、単純にセレクター ボックスの左上隅から開始し、ヒット テストしてどのタイルを見つけ、それらの間隔に従って次のタイルに移動することはできませんか。

たとえば、タイルが 32x16 の場合、角から始めて 32 に沿って移動し、最後に到達したら次の行に沿って進みます。

タイル間隔

たとえば、奇妙な種類の疑似コードで...

var altRow = false;
var selectedTiles = [];
for (int y = selectorBox.top; y < selectorBox.top+selectorBox.height; y+=TILE_HEIGHT/2) {
    for (int x = selectorBox.left ; x < selectorBox.left+selectorBox.width; x+= TILE_WIDTH) {
        selectedTiles.add(tileAtPoint(x + (altRow ? - TILE_WIDTH/2 : 0), y);
    }
    altRow = !altRow;
}
于 2011-08-15T14:39:22.757 に答える
2

他の誰も見たことのない私のアプローチ: 1. 選択した四角形をグリッドに変換します (各コーナーのタイル位置を取得します) 2. 2 次元の問題があります。ひし形。

これは、次のように行うことができます: http://en.wikipedia.org/wiki/Flood_fill

しかし、次のような方がよいかもしれません。

1. Start at the topmost
2. Go down
3. Go right until we encounter the edge (can be calculated from the rombus line)
4. Do the same thing but go left.
5. Go to 2 until we are at the middle
6. Go right and down.
7. Go right until we encouter the edge
8. Go left and down.
9. Go left until we encouter the edge
10. Go to 6 until we are at the bottom

訪問するすべてのタイルで、そのタイルを追加します。

残っている唯一の問題は、エッジに遭遇したかどうかを判断することです...

于 2011-08-15T20:32:59.177 に答える
2

等角グリッドの四角形にマッピングされた選択ボックスの角の座標を取得できると思います。これにより、ソリューションの簡単な代数テストを使用してプロットの問題が得られます。等平方根 (x1,y1) と (x2,y2) の角から始めると、これらの点を通る勾配 1 と -1 の線に沿ってテストする必要があることがわかります。

したがって、4 つの線は y-y1=x-x1、y-y1=x1-x、y-y2=x-x2、y-y2=x2-x です。それらは、最初に選択したボックスの角を含むアイソスクエアで出会います。

便宜上、x2 と y2 がそれぞれ x1 と y1 よりも大きいと仮定すると、x1 から x2 まで、および y1 から y2 までの値についてアイソグリッドを反復処理するだけで済み、y 座標が以下のアイソスクエアのみを受け入れます。 「大きい」線の両方と、2 つの「小さい」線よりも小さい。

于 2011-08-17T00:06:28.580 に答える