XNAで六角形のタイルマップピッキングを実装するにはどうすればよいですか?


13

六角形がクリックされたときに確認する必要がある六角形のタイルマップがあります。六角形は実際には接触しておらず、それぞれの間にわずかな隙間があります。

全体を複雑にすることなく、六角形がクリックされたかどうかを確認する方法を知っている人はいますか?

回答:


13

この写真を見てください

六方分解

ご覧のとおり、x、y直交座標系を六角形にマッピングする比較的直感的な方法があります。

「長方形」の不規則な六角形、つまり楕円に内接する六角形、または両方向に不均衡にスケーリングされた通常の六角形から得られる六角形(回転せん断なし)について話すことができます。

長方形は、外接する長方形の高さと幅に内接する長方形の幅を加えて定義できます。(W、w、h)

四角形の内接/外接

六角形のインデックスを見つける最も簡単な方法は、次のようにスペースを分割することです。

スペースパーティション

長方形の幅はw +(W-w)/ 2 =(w + W)/ 2で、高さはh / 2です。緑の長方形の幅は(Ww)/ 2です。ポイントがどの長方形のどこにあるかを見つけるのは簡単です:

ここに画像の説明を入力してください

uとvは、ポイントがi、j長方形のどこにあるかを示すリマインダ座標です。wを使用すると、緑の領域(u <(Ww)/ 2)にいるかどうかがわかります。

緑の領域にいる場合は、六角形の上半分か下半分かを知る必要があります。iとjが両方とも偶数か、両方とも奇数であれば、上半分にいます。そうでない場合は下半分になります。

どちらの場合も、uとvを0から1の間で変化させると便利です。

ここに画像の説明を入力してください

下半分 v <uの場合

または

上半身(1-v)> uの場合

次に、iを1減らします

iが水平の六角形のインデックス(列)であり、j / 2の整数部分が垂直の六角形のインデックス(行)であることを確認するために、iが奇数の場合は、jを1減らすだけです。

ここに画像の説明を入力してください


ありがとう!本当に役立ちました。緑のセクションの分割でいくつかの問題が発生しました(iから1を引くかどうか)が、六角形の向きによるものだと思います。すべての作業、ありがとう!
ジョエル

14

通常の六角形には対称軸が6つありますが、六角形には対称軸が2つしかないと仮定します(つまり、すべての角度が正確に60度ではありません)。必ずしもあなたの完全な対称性を持っているからではなく、他の誰かに役立つかもしれないからです。

1つの六角形のパラメーターを以下に示します。その中心はO、最大の幅は2a、高さは2b、上辺の長さは2cです。

         Y ^
           |
       ____|____
      /  b |   |\
     /     |   | \
    /      |   |  \
---(-------+---+---)------>
    \     O|   c  / a      X
     \     |     /
      \____|____/
           |

これは行/列レイアウトであり、原点は左下の六角形の中心にあります。設定が異なる場合は、(x,y)座標を変換してこのケースにフォールバックするか、たとえば次の-y代わりに使用yします。

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/  \__/  \__/  \__/  \__/  \__
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/_ _ line 2
\__/  \__/  \__/  \__/  \__/  \ _ _ _ line 1
/ .\__/  \__/  \__/  \__/  \__/_ _ line 0
\__/  \__/  \__/  \__/  \__/

次のコードは、ポイントを含む六角形の行と列を提供します(x,y)

static void GetHex(float x, float y, out int row, out int column)
{
  // Find out which major row and column we are on:
  row = (int)(y / b);
  column = (int)(x / (a + c));

  // Compute the offset into these row and column:
  float dy = y - (float)row * b;
  float dx = x - (float)column * (a + c);

  // Are we on the left of the hexagon edge, or on the right?
  if (((row ^ column) & 1) == 0)
      dy = b - dy;
  int right = dy * (a - c) < b * (dx - c) ? 1 : 0;

  // Now we have all the information we need, just fine-tune row and column.
  row += (column ^ row ^ right) & 1;
  column += right;
}

上記のコードがこのIdeOne実行で完全な六角形を描くことを確認できます


ideOneについて初めて聞いたことがありますが、本当に便利なようです!
デヴィッドゴーベイア

@davidluzgouveia:はい、それは素晴らしいです。私はウェブやクラウドサービスが好きではありませんが、これは役に立ちます。
サムホセバル

1

3つの回転した長方形を六角形の領域内に収めることができ、適切に行われた場合、領域を正確に埋めることができます。次に、3つの長方形の衝突をチェックするだけです。


1

おそらく、タイル間のクリックの登録を解除する必要はありません。つまり、論理的にはいけない何かで満たされている大きなスペースについて話しているのでない限り、タイル間のスペースもクリックできるようにすると、害はなく、プレーヤーを助けることさえありますクリックされます。(たとえば、ヘクスは大きなマップ上の都市で、その間には他のクリック可能なものがあります)

上記を行うには、すべてのヘックスの中心をプロットし、すべてのヘックスの平面をクリックしたときにマウスに最も近いものを見つけることができます。テッセレーションされた六角形の平面上の最も近い中心は、常にホバーしている同じ中心になります。


1

Stack Overflowについて、同じ目標で、同様の質問に既に答えました。利便性のためにここに再投稿します。

正方形グリッドがオーバーレイされた六角形グリッド

この画像は、六角形のグリッドの左上隅を示しており、オーバーレイは青い正方形のグリッドです。ポイントがどの正方形の中にあるかを見つけるのは簡単で、これはどの六角形の大まかな近似を与えるでしょう。六角形の白い部分は正方形と六角形のグリッドが同じ座標を共有する場所を示し、六角形の灰色の部分は共有しない場所を示します。

解決策は、ポイントがどのボックスにあるかを見つけ、ポイントが三角形のいずれかにあるかどうかを確認し、必要に応じて回答を修正するだけです。

private final Hexagon getSelectedHexagon(int x, int y)
{
    // Find the row and column of the box that the point falls in.
    int row = (int) (y / gridHeight);
    int column;

    boolean rowIsOdd = row % 2 == 1;

    // Is the row an odd number?
    if (rowIsOdd)// Yes: Offset x to match the indent of the row
        column = (int) ((x - halfWidth) / gridWidth);
    else// No: Calculate normally
        column = (int) (x / gridWidth);

この時点で、ポイントが入っているボックスの行と列があります。次に、ポイントを六角形の2つの上端に対してテストして、ポイントが上の六角形のいずれかにあるかどうかを確認する必要があります。

    // Work out the position of the point relative to the box it is in
    double relY = y - (row * gridHeight);
    double relX;

    if (rowIsOdd)
        relX = (x - (column * gridWidth)) - halfWidth;
    else
        relX = x - (column * gridWidth);

相対座標があると、次のステップが簡単になります。

直線の一般的な方程式

上の画像のように、ポイントのy> mx + cである場合、ポイントはラインより上にあり、この場合、現在の行と列の左上にある六角形であることがわかります。javaの座標系のyは画面の左上で0から始まり、数学で通常の左下ではないことに注意してください。したがって、左端に負の勾配が使用され、右に正の勾配が使用されます。

    // Work out if the point is above either of the hexagon's top edges
    if (relY < (-m * relX) + c) // LEFT edge
        {
            row--;
            if (!rowIsOdd)
                column--;
        }
    else if (relY < (m * relX) - c) // RIGHT edge
        {
            row--;
            if (rowIsOdd)
                column++;
        }

    return hexagons[column][row];
}

上記の例で使用されている変数の簡単な説明:

ここに画像の説明を入力してください ここに画像の説明を入力してください

mは勾配なので、m = c / halfWidth


NeoShamamの上記への追加

これは、SebastianTroyの回答の補遺です。コメントとして残しておきますが、まだ評判が十分ではありません。

ここで説明されている軸座標系を実装する場合:http : //www.redblobgames.com/grids/hexagons/

コードにわずかな変更を加えることができます。

の代わりに

// Is the row an odd number?
if (rowIsOdd)// Yes: Offset x to match the indent of the row
    column = (int) ((x - halfWidth) / gridWidth);
else// No: Calculate normally
    column = (int) (x / gridWidth);

これを使って

float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way

これにより、座標(0、2)は(0、0)の直下ではなく、(0、0)および(0、1)と同じ対角列になります。


これは六角形間のギャップを考慮に入れていないことを理解していますが、問題をかなり絞り込むのに役立つはずです。
トロイセフ

0

すべての六角形が同じプロポーションと配置を使用して作成されている場合、衝突に何らかの種類のオーバーレイアセットを使用できます。 六角形の衝突オーバーレイ

次に、あなたがしなければならないのは、六角形がある場所に衝突画像を配置し、左隅に対するマウスの位置を取得し、相対位置のピクセルが白ではないかどうかを確認することです(つまり、衝突があることを意味します)。

コード(テストなし):

bool IsMouseTouchingHexagon(Vector2 mousePosition, Vector2 hexagonPosition,
    Rectangle hexagonRectangle, Texture2D hexagonImage)
{
    Vector2 mousePositionToTopLeft = mousePosition - hexagonPosition;

    // We make sure that the mouse is over the hexagon's rectangle.
    if (mousePositionToTopLeft.X >= 0 && mousePositionToTopLeft.X < hexagonRectangle.Width && 
        mousePositionToTopLeft.Y >= 0 && mousePositionToTopLeft.Y < hexagonRectangle.Height)
    {
        // Where "PixelColorAt" returns the color of a pixel of an image at a certain position.
        if (PixelColorAt(hexagonImage, mousePositionToTopLeft) == Color.White)
        {
            // If the color is not white, we are colliding with the hexagon
            return true;
        }
    }

    // if we get here, it means that we did not find a collision.
    return false;
}

プロセス全体のパフォーマンスを向上させるために、事前に(六角形イメージ全体の)長方形の衝突チェックを事前に実行できます。

この概念は理解と実装が非常に簡単ですが、六角形がすべて同じ場合にのみ機能します。また、六角形の寸法のセットしか持っていない場合にも機能する可能性があります。つまり、複数のコリジョンオーバーレイが必要になります。

はるかに完全で再利用可能なものに対する非常に単純なソリューションであることがわかったら(数学を使用して実際に衝突を見つける)、それは私の意見で試してみる価値があります。


あなたの方法で、不規則な形状のセットを使用して、すべてにユニークな色のオーバーレイを与え、オーバーレイしているオブジェクトに色をマップすることができますので、オーバーレイバッファから色を選択し、マップを使用してマウスの下のオブジェクト。私がこのテクニックを特に支持しているわけではなく、やや複雑すぎて、私見の時期尚早な最適化の試みに聞こえます。
トロイセフ

0

For Bees and Gamersと呼ばれるGame Programming Gems 7の記事:六角形タイルの処理方法があります。これはまさに必要なものです。

残念ながら、現時点では本のコピーを持っていません。そうでなければ、少し説明することができました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.