0

私は六角形のタイル マップを使用して単純な 2D ボード ゲームを開発しています。画面上に六角形を描画する方法と管理方法に関するいくつかの記事 (六角形のタイルに関する質問があるたびにリンクされている gamedev の記事を含む) を読みました。動き(ただし、その多くは以前に行ったことがあります)。私の主な問題は、指定された半径に基づいて隣接するタイルを見つけることです。

これが私の地図システムの仕組みです:

(0,0) (0,1) (0,2) (0,3) (0,4)    
   (1,0) (1,1) (1,2) (1,3) (1,4)   
(2,0) (2,1) (2,2) (2,3) (2,4)  
   (3,0) (3,1) (3,2) (3,3) (3,4) etc...

私が苦労しているのは、for(x-range;x+range;x++); for(y-range;y+range;y++);不要なタイルを選択するため、使用して隣接するタイルを「選択」することができないという事実です (私が示した例では、(1,1) タイルを選択し、1 の範囲を指定すると、私は (3,0) タイル (実際に必要なものは (0,1)(0,2)(1,0)(1,2)(2,1)(2,2) です)。 (配列が構造化されているため)タイルに隣接していますが、実際には選択したいものではありません.力ずくでそれを実行することもできますが、それは美しいものではなく、おそらく「半径の選択」のすべての側面をカバーするわけではありません. '。

誰かがここで私を正しい方向に向けることができますか?

4

2 に答える 2

6

六角形および直交グリッド

六角形のグリッドとは何ですか?

上に表示されているのは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)36

要約すると。(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)を選択ループに直接含めることができるため、範囲外のタイルを完全に回避する方法でグリッドを反復できます。

于 2013-03-20T06:43:21.613 に答える
3

次のようにグリッドをレイアウトするだけです。

   +-----+-----+-----+-----+
   | 0   |  1  | 2   |  3  |          even row
   +-----+-----+-----+--+-----+
      |  4  |  5  |  6  |  7  | ==>   odd row
   +--+-----+-----+-----+--+--+
   |  8  |  9  |  10 |  11 |          even row
   +-----+-----+-----+-----+

奇数行(N>>2)&1 == 1の場合、概念的にスロット幅の半分だけスロットをシフトする必要があります。
その場合、隣人は { N-1, N+1, Na, N-a+1, N+b, N+b+1 }, a=b=4 です。
偶数行の場合、a=5、b=3。(a=上、b=下)

このアイデアを使用して、隣接リストを作成し、幅優先検索を実行できます (距離のいくつかのメトリック (ユークリッドまたはハニー グリッドで呼ばれるマンハッタン距離) を使用)。

于 2013-03-20T06:47:39.017 に答える