
六角形のグリッドとは何ですか?
上に表示されているのは2つのグリッドです。それはすべて、タイルに番号を付ける方法と、六角形のグリッドが何であるかを理解する方法にあります。私の見方では、六角形のグリッドは変形した直交グリッドにすぎません。
私が紫色で囲んだ2つの六角形のタイルは、理論的にはまだに隣接してい0,0
ます。ただし、直交するグリッドから16タイルグリッドを取得するために行った変形により、2つは視覚的に隣接しなくなりました。
変形
私たちが理解する必要があるのは、[(-1,1) (1,-1)]
私の例では想像上の線に沿って、特定の方向に変形が起こったことです。より正確には、グリッドがその線に沿って引き伸ばされ、それに垂直な線に沿って押しつぶされているかのようです。そのため、当然、その線上の2つのタイルは広がり、視覚的に隣接しなくなりました。逆に、対角線上に(1, 1)
あるタイルは、現在は異常に近くなっているため、実際には非常に近く、視覚的にはに隣接しています。ただし、数学的にはまだ対角線であり、コード内でそのように扱うのに役立ちます。(-1, -1)
(0, 0)
(0, 0)
(0, 0)
選択
私が示す画像は、半径1を示しています。半径2の場合、選択に含めるべきではないタイルに(2, -2)
気付くでしょう。(-2, 2)
等々。したがって、半径rを選択する場合は、ポイント(r, -r)
と(-r, r)
を選択しないでください。それ以外は、選択アルゴリズムは正方形のタイルグリッドと同じである必要があります。
六角形のグリッド上に軸が適切に設定されていることと、それに応じてタイルに番号が付けられていることを確認してください。
実装
これについて少し詳しく見ていきましょう。グリッド内の任意の方向に沿った移動には1.コストがかかり、引き伸ばされた方向に沿った移動には2.コストがかかることがわかりました。たとえば、を参照(0, 0)
してください。(-1, 1)
これを知っていると、距離を2つのコンポーネント(対角線の動きと軸の1つに沿った直線の動き)に分解することによって、そのようなグリッド上の任意の2つのタイル間の最短距離を計算できます。たとえば、通常のグリッド間の距離(1, 1)
とグリッド上の距離については、次のようになります。(-2, 5)
Normal distance = (1, 1) - (-2, 5) = (3, -4)
これは、2つのタイルが正方形のグリッド上にある場合の距離ベクトルになります。ただし、グリッドの変形を補正する必要があるため、次のように分解します。
(3, -4) = (3, -3) + (0, -1)
ご覧のとおり、ベクトルを1つの対角線(3, -3)
と軸に沿った1つの直線に分解しました(0, -1)
。
ここで、対角線が変形軸に沿っているかどうかを確認します。これは、が正または負の整数である
(n, -n)
任意の点です。は確かにその条件を満たすので、この対角ベクトルは変形に沿っています。これは、このベクトルの長さ(またはコスト)が、ではなく2倍になることを意味します。つまり、です。n
(3, -3)
3
6
要約すると。(1, 1)
との間の距離(-2, 5)
は、の長さに(3, -3)
プラスの長さを加えたものです(0, -1)
。それはdistance = 3 * 2 + 1 = 7
です。
C++での実装
以下は、上記で説明したアルゴリズムのC++での実装です。
int ComputeDistanceHexGrid(const Point & A, const Point & B)
{
// compute distance as we would on a normal grid
Point distance;
distance.x = A.x - B.x;
distance.y = A.y - B.y;
// compensate for grid deformation
// grid is stretched along (-n, n) line so points along that line have
// a distance of 2 between them instead of 1
// to calculate the shortest path, we decompose it into one diagonal movement(shortcut)
// and one straight movement along an axis
Point diagonalMovement;
int lesserCoord = abs(distance.x) < abs(distance.y) ? abs(distance.x) : abs(distance.y);
diagonalMovement.x = (distance.x < 0) ? -lesserCoord : lesserCoord; // keep the sign
diagonalMovement.y = (distance.y < 0) ? -lesserCoord : lesserCoord; // keep the sign
Point straightMovement;
// one of x or y should always be 0 because we are calculating a straight
// line along one of the axis
straightMovement.x = distance.x - diagonalMovement.x;
straightMovement.y = distance.y - diagonalMovement.y;
// calculate distance
size_t straightDistance = abs(straightMovement.x) + abs(straightMovement.y);
size_t diagonalDistance = abs(diagonalMovement.x);
// if we are traveling diagonally along the stretch deformation we double
// the diagonal distance
if ( (diagonalMovement.x < 0 && diagonalMovement.y > 0) ||
(diagonalMovement.x > 0 && diagonalMovement.y < 0) )
{
diagonalDistance *= 2;
}
return straightDistance + diagonalDistance;
}
これで、上記の実装されたComputeDistanceHexGrid
関数が与えられると、指定された選択範囲を超えるタイルを無視する選択アルゴリズムの素朴で最適化されていない実装を行うことができます。
int _tmain(int argc, _TCHAR* argv[])
{
// your radius selection now becomes your usual orthogonal algorithm
// except you eliminate hex tiles too far away from your selection center
// for(x-range;x+range;x++); for(y-range;y+range;y++);
Point selectionCenter = {1, 1};
int range = 1;
for ( int x = selectionCenter.x - range;
x <= selectionCenter.x + range;
++x )
{
for ( int y = selectionCenter.y - range;
y <= selectionCenter.y + range;
++y )
{
Point p = {x, y};
if ( ComputeDistanceHexGrid(selectionCenter, p) <= range )
cout << "(" << x << ", " << y << ")" << endl;
else
{
// do nothing, skip this tile since it is out of selection range
}
}
}
return 0;
}
選択ポイント(1, 1)
と範囲の1
場合、上記のコードは期待される結果を表示します。
(0, 0)
(0, 1)
(1, 0)
(1, 1)
(1, 2)
(2, 1)
(2, 2)
可能な最適化
これを最適化するために、タイルが選択ポイントからどれだけ離れているかを知るロジック(にあるロジックComputeDistanceHexGrid
)を選択ループに直接含めることができるため、範囲外のタイルを完全に回避する方法でグリッドを反復できます。