Simple hex picker

Using hexes instead of square fields gives strategy games more depth and variety in creating interesting, tactical situations. But, because of its shape, it is slightly more challenging to implement them. This article will show how to nicely pick the proper hex tile using a mouse.

First of all, let’s look at the interesting properies of the hexagon.


For now we will be implementing the first variant.

The edge of the hexagon has the same length as the radius of the circle in which it fits. It is valuable information, because in many graphic libraries hexes can be drawn as a 6 points circles (which is very convenient).

Other values from this image, which can be easily calculate from the radius, will be used in the next parts of this article.

Let’s look at the hex grid.


All hexes are stored in columns and rows and indexed as [column;row]. But if we want to pick them using mouse position we need to divide whole grid into the rectangles like this:


Now it is easy to determine in which column and row the mouse was clicked.
int column = (int)(mouseX / B);
int row = (int)(mouseY / (R+C));
Every area contains parts of only two hexagons. Now we have to determine which one was clicked.

Let’s say we’ve clicked the [2;0] hex, in column nr 4 and row nr 1. In this area the [2;0] can be clicked or the [1;1]. How to tell which one was clicked?

The border between those two hexes is in fact a linear function which the equation can be calculated, because we know the length and angles of the hexagon:

To make a clearer view let’s just look at the border only:


dx and dy are the distances from the left-top corner of the selected by mouse area to the place where the mouse was clicked.
float dx = mouseX - (float)column * B;
float dy = mouseY - (float)row * (R + C);
The equation of this function is: y = (C*x) / B

So to determine if the click was above this line this condition has to be true
dy < ((C * dx) / B)
But clearely this is true only for borders directed in this way, but half of the areas have borders directed in the opposite direction. In fact for those areas we have only reverse the dx value:
dx = B - dx;
To easily determine which areas needs this modification is to check this condition
((row ^ column) & 1) == 0;
Areas that fits with this conditions are highlighted in grey


Now when we know which area and which part of area was clicked we can fine-tune the coordinates to fit the original coordinates of the grid. The whole calculation will look like this:
if (((row ^ column) & 1) == 0)
{
    dx = B - dx;
}
int top = (dy < ((C * dx) / B)) ? 1 : 0;
 
column -= (row ^ column ^ top) & 1;
column *= .5;
row -= top;
And that's it. Now we have an index of [column;row] of the hex that was clicked.

You can download a source of the application on GitHub, or just get a working .exe application from here.