Skip to main content
GameDev.net gamedev.net
🔒 Locked

Mouse to hex coordinates - again.

Started by Talin Jun 7, 2009 at 12:53 PM 9 replies 5.5k views
Original Post
Talin
Talin
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).
Wyrframe
Wyrframe
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 [ code ][/ code ] 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.
Talin
Talin
Thanks a lot for the help, the code seems pretty clean. I'll try it out today, had a busy week. :)
Mark Butler
Mark Butler
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.<br/><br/>So, to see if the point slope is less than the hexagon slope<br/><br/>is ( y / x ) less than ( rise / run ) check now becomes<br/><br/>is ( y * run ) less than ( rise * x ) check<br/><br/>this avoids dividing integers and having to deal with fractions.<br/><br/>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>
Mark Butler
Mark Butler
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
Mark Butler
Mark Butler
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]
c-Row
c-Row
Couldn't you just calculate which hex field's middle coordinate is closest to the point of mouse click via Phytagoras' theorem?
Wyrframe
Wyrframe
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.
c-Row
c-Row
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.
Wyrframe
Wyrframe
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.
Mark Butler
Mark Butler
I chop up the map into rectangles, just like the example above.
Then, use the slope to figure out if the point is left/right, up/down of the hexagon.
Except I chop them up into smaller pieces, so the cursor can also identify hexsides.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.