回答:
この写真を見てください
ご覧のとおり、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減らすだけです。
通常の六角形には対称軸が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実行で完全な六角形を描くことを確認できます。
おそらく、タイル間のクリックの登録を解除する必要はありません。つまり、論理的にはいけない何かで満たされている大きなスペースについて話しているのでない限り、タイル間のスペースもクリックできるようにすると、害はなく、プレーヤーを助けることさえありますクリックされます。(たとえば、ヘクスは大きなマップ上の都市で、その間には他のクリック可能なものがあります)
上記を行うには、すべてのヘックスの中心をプロットし、すべてのヘックスの平面をクリックしたときにマウスに最も近いものを見つけることができます。テッセレーションされた六角形の平面上の最も近い中心は、常にホバーしている同じ中心になります。
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
これは、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)と同じ対角列になります。
すべての六角形が同じプロポーションと配置を使用して作成されている場合、衝突に何らかの種類のオーバーレイアセットを使用できます。
次に、あなたがしなければならないのは、六角形がある場所に衝突画像を配置し、左隅に対するマウスの位置を取得し、相対位置のピクセルが白ではないかどうかを確認することです(つまり、衝突があることを意味します)。
コード(テストなし):
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;
}
プロセス全体のパフォーマンスを向上させるために、事前に(六角形イメージ全体の)長方形の衝突チェックを事前に実行できます。
この概念は理解と実装が非常に簡単ですが、六角形がすべて同じ場合にのみ機能します。また、六角形の寸法のセットしか持っていない場合にも機能する可能性があります。つまり、複数のコリジョンオーバーレイが必要になります。
はるかに完全で再利用可能なものに対する非常に単純なソリューションであることがわかったら(数学を使用して実際に衝突を見つける)、それは私の意見で試してみる価値があります。
For Bees and Gamersと呼ばれるGame Programming Gems 7の記事:六角形タイルの処理方法があります。これはまさに必要なものです。
残念ながら、現時点では本のコピーを持っていません。そうでなければ、少し説明することができました。