Mouse to hex coordinates - again.

Started by
9 comments, last by Mark Butler 14 years, 10 months ago
Right, I know this has been asked a dozen of times, but none of the threads or articles I dug out actually helped me get it right, so this thread is pretty much my last hope. :) So, the hexes are lined up thusly: 0,0 ------- 0,1 ------- 0,2 ----- 1,0 ------- 1,1 ----- 2,0 ------- 2,1 ------- 2,2 The numbers are hex x,y coordinates. What complicates things a little is that they are lined up vertically, and that the tiles are 20 x 16, like this: __ /..\ 16 \__/ 20 (4+12+4) So, typically, I have the mouse coordinates of where the user clicked, and using that I need to find the actual hex x,y the user clicked on. If it helps, this is the best article I found (that is, closest to what I actually need), but I still couldn't implement that properly. It not only gives me the wrong coordinates, it also doesn't have consistent values for a single hex. What I'd like is some specific formula for my own tile size and dimensions, without any scrolling offset considerations (consider the map to be unscrollable).
Advertisement
I do it with just three quick constants; HEX_SLOPE is the rise/run ratio of your side ascent (in your case, 8/4), TILE_WIDTH is one slope plus the flat section (in your case, 16), and TILE_HEIGHT is the full tile height (again, 16).

This works by breaking columns up into squares of the following...
 _____ _____| /   |\    ||/    | \___||\    | /   ||_\___|/____|
You can see that evenly tiles to cover a hex grid; so then I just run the math to find which of the three sectors in a given tile the point is in.

Some fairly easy-to-translate Java.
    public static void getHexAtPoint (double aX, double aY, Point2D aReturn)    {        final long gX = (long) Math.floor (aX / TILE_WIDTH);        final long gY = (long) Math.floor (aY / TILE_HEIGHT);        final double fX = (aX / TILE_WIDTH) - gX;        final double fY = (aY / TILE_HEIGHT) - gY;                final long retX;        final long retY;                if( (Math.abs (gX) & 1) == 1 )        {            if( fY > 0.5 )            {                if( 1.0 - fX * HEX_SLOPE < fY )                {                    retX = gX;                    retY = gY;                }                else                {                    retX = gX - 1;                    retY = gY;                }            }            else            {                if( fX * HEX_SLOPE > fY )                {                    retX = gX;                    retY = gY - 1;                }                else                {                    retX = gX - 1;                    retY = gY;                }            }        }        else        {            if( fY > 0.5 )            {                if( fY - 0.5 < fX * HEX_SLOPE )                {                    retX = gX;                    retY = gY;                }                else                {                    retX = gX - 1;                    retY = gY;                }            }            else            {                if( fY - 0.5 > fX * -HEX_SLOPE )                {                    retX = gX;                    retY = gY;                }                else                {                    retX = gX - 1;                    retY = gY - 1;                }            }        }                aReturn.setLocation (retX, retY);    }

BTW: use tags for fixed-width but unadorned text, and [ source lang="java|cpp"][/ source ] tags for scrolling syntax-highlighted text.

EDIT: crap, wait. You use side-offset rows instead of down-offset columns. You'll have to change the +/- constants (ie retX = gX + 1), but otherwise it should still work for you.
RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
Thanks a lot for the help, the code seems pretty clean. I'll try it out today, had a busy week. :)
If you are programing in &#106avascript and just want to use integer math, you can alter the slope comparison statement by mutiplying by the denominators.

So, to see if the point slope is less than the hexagon slope

is ( y / x ) less than ( rise / run ) check now becomes

is ( y * run ) less than ( rise * x ) check

this avoids dividing integers and having to deal with fractions.

I also calculate a mapgrid on the hexagon map for placing counters on the hexagon map and calculating ranges, bearing and line of sight. Sample code in &#106avascript and C# at my <a href="http://www.webwargaming.org/mapgrid">http://www.webwargaming.org</a>
Here is what the mapgrid would look like


Movement is easily calculated with simple math.

North x + 0, y - 4
Northeast x + 2, y - 2
Southeast x + 2, y + 2
South x + 0, y + 4
Southwest x - 2, y + 2
Northwest x - 2, y - 2
Range is now easy

if ( absX > absY ) {
range = absX / 2;
} else {
range = ( absX + absY ) / 4;
}

for example, from 0101 ( 4, 8 ) to 0404 ( 10, 22 )

absX = Math.abs( 10 - 4 ) = 6
absY = Math.abs( 22 - 8 ) = 14

range = ( 6 + 14 ) / 4 = 20/4 = 5 !!


range demo

[Edited by - Mark Butler on June 26, 2009 9:59:40 AM]
Couldn't you just calculate which hex field's middle coordinate is closest to the point of mouse click via Phytagoras' theorem?
c-Row; you still need a reliable approximation of which hexes your cursor is actually nearest to, unless you want to test every hex. Two float-to-int truncations, two integer multiplies, and two integer additions is much cheaper than (best case scenario) twelve floating-point subtractions, and six floating-point multiplications; that's for three range compares, one for each of the theoretically nearest hexes.

Determining which four hexes are nearest to the cursor costs at least two multiplies and two subtractions... and the natural reaction to that is "hey, wait a minute..."
RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.
My approaches might not be elegant, but they are easier to understand. ;) I don't know what absX is for a start.

Given that a hex is, say 50 pixels high and 50 pixels wide, you just need to find those whose coordinates are equal or less than 25 pixels away from the mouse. If you are more than 25 pixels away from the center, you are not within the hex anyway. Just from the top of my head, actual pixel measurements might vary, but the general idea is the same.
Right, I know, I follow you. The thing is; how do you determine the position of those nearest hex's centers? By dividing by the hex height, to get the row, and dividing by most of the hex width, to get the column.

At that point, instead of calcuating the center position of three hexes and then comparing the distances to them, you can instead just test being above or below a slope, a calculation only taking a multiply and an add or two.
RIP GameDev.net: launched 2 unusably-broken forum engines in as many years, and now has ceased operating as a forum at all, happy to remain naught but an advertising platform with an attached social media presense, headed by a staff who by their own admission have no idea what their userbase wants or expects.Here's to the good times; shame they exist in the past.

This topic is closed to new replies.

Advertisement