привязка к ближайшему центру шестиугольника в сетке на основе шестиугольника

Я пытаюсь создать игру на основе сетки. пока что у меня есть сетка на основе гексагональной плитки с координатной схемой, как показано ниже:

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/00\__/02\__/04\__/06\__/08\__
\__/01\__/03\__/05\__/07\__/09\--- row 0
/10\__/12\__/14\__/16\__/18\__/
\__/11\__/13\__/15\__/17\__/19\--- row 1
/20\__/22\__/24\__/26\__/28\__/
\__/21\__/23\__/25\__/27\__/29\--- row 2
/30\__/32\__/34\__/36\__/38\__/
\__/  \__/  \__/  \__/  \__/   --- row 3

И в реальной жизни выглядит так, только со случайными цветами для каждого шестиугольника:

Я изо всех сил пытаюсь понять, когда пользователь нажимает на шестиугольник, как мне определить, на какой шестиугольник он щелкнул?

код, который я пробовал до сих пор, выглядит следующим образом:

private: System::Void MyForm_MouseDown(System::Object^  sender,
    System::Windows::Forms::MouseEventArgs^  e) {

    int CloseI=0,CloseJ=0;
    CloseJ = FindNearesetX(e->X);
    CloseI = FindNearesetY(e->Y);
    //Grid[down(y)][along(x)]
    P1.X = Grid[CloseI][CloseJ].GetX();
    P1.Y = Grid[CloseI][CloseJ].GetY();
} // END MOUSE DOWN EVENT

int FindNearesetX(int ActualX){
    int ClosestJPos;
    ClosestJPos = ((ActualX-Grid[0][0].GetX())/(1.5*HexSideLength));
    return ClosestJPos;
}//END FIND NEAREST X

int FindNearesetY(int ActualY){
    int ClosestIPos;
    ClosestIPos = ((ActualY-Grid[0][0].getY())/(HexHeight));
    return ClosestIPos;
}//END FIND NEAREST Y

private: System::Void MyForm_MouseMove(System::Object^  sender,
    System::Windows::Forms::MouseEventArgs^  e) {
    this->Invalidate();

    P2.X = e->X;
    P2.Y = e->Y; 
} // END MOUSE MOVE EVENT       

Однако это не сработало так, как я хотел, потому что, когда пользователь щелкает слева от центральной точки шестиугольника, он привязывается к шестиугольнику слева от того, по которому они щелкнули, а также, если они щелкают над центральной точкой на все нечетные столбцы он привязывает к шестиугольнику над тем, на котором они щелкнули.

Я застрял на этом уже 2 дня и очень хочу разобраться. Спасибо


person JabbaWook    schedule 04.03.2014    source источник
comment
Прошло 2 года с тех пор, как я разместил этот вопрос, но я помню, что видел этот пост, и я считаю, что он не решил мою проблему (хотя я уверен, что это помогло), однако я слишком долго не мог вспомнить подробности о том, почему он не ответил на мой вопрос. Странно видеть активность в этом посте после такого долгого времени! Ага   -  person JabbaWook    schedule 16.05.2016


Ответы (4)


Точка щелчка всегда будет ближе всего к центру шестиугольника, в котором происходит щелчок, если только точка не находится точно между двумя шестиугольниками, и в этом случае она будет равноудалена от двух центров. Уравнение расстояния между двумя точками - это КОРЕНЬ ((x1-x2) ^ 2 + (y1-y2) ^ 2).

Вам не нужно проверять расстояние до каждого шестиугольника. Создавая пороговые значения x / y, вы можете ограничить тест только ближайшими шестиугольниками. Например, если шестиугольники имеют ширину 10 и точка находится в точке (51, 73), вам не нужно тестировать шестиугольники с x-координатами ‹40 или> 70.

person Tyler Durden    schedule 04.03.2014
comment
Кроме того, поскольку вы просто заинтересованы в поиске ближайшего шестиугольника, вы должны иметь возможность опустить проверку квадратного корня, поскольку фактическая длина ни для чего не будет использоваться. Это может сэкономить вам несколько циклов, если вы проверяете много полигонов. Точка отсечения по ширине, предложенная Тайлером, также является очень быстрой оптимизацией, которую вам следует рассмотреть, и ее обязательно следует использовать в качестве предварительного теста. - person David Peterson; 04.03.2014
comment
Правильно, я включил sqrt для полноты картины. В реальной реализации вам не нужно делать sqrt, просто сумму квадратов разностей, которая является дисперсией между точками. - person Tyler Durden; 04.03.2014

Нужно уметь найти ближайший шестиугольник со сложностью O (1):

    odd     even    odd    even
 0 +----+  |    |  +----+  |
   | 00 |\ |    |  | 02 |  |
   |    | \+----+  |    |  +
   |    | /| 01 |  |    |  |
 H +----+/ |    |  +----+  |
   | 10 |\ |    |  | 12 |  |
   |    | \+----+  |    |  +
   |    | /| 11 |  |    |  |
2H +----+/ |    |  +----+  |
   0....X..W.......2W......3W

Углы «+» также являются углами шестиугольников. 'x' DIV 2 * W и y DIV H определяют правильный квадрат, когда 'x' mod W ‹X. Когда W‹ = x mod 2W ‹= W + X, точка располагается на четных столбцах. В нечетных столбцах номер строки - y DIV H, в четных столбцах - (y + H / 2) РАЗДЕЛ H.

Серая область, плохо проиллюстрированная зигзагообразным узором, требует решения двух линейных уравнений (или скалярного произведения), чтобы определить, на какую сторону диагонали падает точка. В любом случае есть максимум два кандидата на выбор.

person Aki Suihkonen    schedule 04.03.2014

Фактически, это можно сделать довольно легко математически, не прибегая к раздражающему, ограничивающему масштаб методу перебора большого количества потенциальных значений. Я придумал следующий код в сговоре с отличной информацией на следующем веб-сайте. Секрет в том, чтобы представить, что ваша шестиугольная сетка на самом деле представляет собой плоскость из трехмерных кубов.

http://www.redblobgames.com/grids/hexagons/

N.B. SS2DCoordinates и SS3DCoordinates - это простые структуры с двумя или тремя целочисленными переменными, представляющими координаты в 2D и 3D сетке соответственно (x / y для 2D, x / y / z для 3D). Также обратите внимание, что моя шестнадцатеричная сетка начинается с 1/1, а не с 0 / 0.

SS2DCoordinates coordinatesForHexAtPoint(float a, float b)
{
    // Get basic hex information - pseudocode
    float radius = <radius of one hexagon>

    // Estimate the most likely hex and round to nearest values
    float x = 2.0/3.0*a/radius;
    float z = (1.0/3.0*sqrt(3.0)*b-1.0/3.0*a)/radius;
    float y = -x-z;

    int ix = (int)round((floor(x-y)-floor(z-x))/3.0);
    int iy = (int)round((floor(y-z)-floor(x-y))/3.0);
    int iz = (int)round((floor(z-x)-floor(y-z))/3.0);

    // Adjust to flat coordinates on the offset numbering system
    SS2DCoordinates corrected = hexToFlatCoordinates(SS3DCoordinatesMake(ix, iy, iz));
    corrected.x --;
    return axialToOffsetCoordinates(corrected);
}


SS2DCoordinates hexToFlatCoordinates(SS3DCoordinates hex)
{
    SS2DCoordinates coordinates;
    coordinates.x = hex.x;
    coordinates.y = hex.z;
    return coordinates;
}


SS2DCoordinates axialToOffsetCoordinates(SS2DCoordinates axial)
{
    SS2DCoordinates offset;
    offset.x = axial.x;
    offset.y = axial.y + (NSInteger)ceilf((float)axial.x/2.0);
    return offset;
}
person Ash    schedule 04.04.2014

На самом деле, из-за правильной формы шестиугольников (все стороны имеют одинаковую длину) это так же просто, как прокручивать список шестиугольных плиток и выяснять, центр какой плитки находится ближе всего к щелчку мыши.

Псевдокод C ++:

//assuming "map" is an array of "Tile" pointers

Tile *closest = nullptr;
int fromClosestCenterToClick = INT_MAX;

for (int row = 0; row < map.numRows(); row++)
{
   for (int col = 0; col < map.numCols(); col++)
   {
      int distance = std::sqrt(std::pow(map[row][column]->center.x - mouseClickX, 2) + std::pow(map[row][column]->center.y - mouseClickY, 2) < fromClosestCenterToClick);
      if (distance < fromClosestCenterToClick)
      {
         closest = map[row][column];
         fromClosestCenterToClick = distance;
      }
   }
}
//closest now holds the correct tile
person Proxy    schedule 04.03.2014
comment
ага, ты скопировал мой ответ - person Tyler Durden; 04.03.2014
comment
@TylerDurden xD Я, наверное, первым начал печатать, но да. По иронии судьбы, ваш более полный. Пороги - хорошая идея. +1 - person Proxy; 04.03.2014