Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

As I have been building on a RTS game based on hexagon grid built in javascript. I stumbled across a problem regarding the coordinate system. Have been trying to implement a A-star system to find paths. But it seems a horizontal hex gird could cause some problems so far when it comes to odd and even.

It was made horizontal as it is the way it is rendered to make all graphics not overlapping improper.

So now to my question after some digging on the web. How do you convert between horizontal and 60 degrees coords?

Illustration of how to be coverted

Have tried this code, but its not good enough, and stuck at what Im missing for my two functions GetWorldXpos and GetWorldYpos.



function DrawHex(context, hexagonData, isEven, x, y)
{
    var heightValue;

    heightValue =  GetIntNoDecimals(hexagonData.heightValue); //parseInt(hexagonData.heightValue, 10);

     //sets up so if the height of sea-levels is achived it should be equal level                 
    heightValue = CheckForLakeLevel(heightValue);

     //Gets color code depengin on height
     var colorCode = GetColorCode(heightValue, hexagonData.heightValue);

    if(isEven){         
        //creates and draws object
        var hex = new HexagonObj(GetLargeMapXpos(x), GetLargeMapYpos(y), GetWorldXpos(x + 1, y), GetWorldYpos(x, y));
        hex.Draw(context, 0.99, lmapData.spaceing, colorCode, (heightValue), 0);

        //Adds data for usage how to draw other worl dependent objects
        lmapHeightData.push(new LargeGridData(GetLargeMapXpos(x), GetLargeMapYpos(y), GetWorldXpos(x, y), GetWorldYpos(x, y), heightValue));
    }
    else
    {
        //creates and draws object
        var hex = new HexagonObj(GetLargeMapXpos(x), GetLargeMapYpos(y) + lmapData.spaceing, GetWorldXpos(x, y), GetWorldYpos(x, y));
        hex.Draw(context, 0.99, lmapData.spaceing, colorCode, (heightValue), 0);

        lmapHeightData.push(new LargeGridData(GetLargeMapXpos(x), GetLargeMapYpos(y) + lmapData.spaceing, GetWorldXpos(x, y), GetWorldYpos(x, y), heightValue));
    }
}


//Gets what is the coordinate to be drawn on screen
function GetLargeMapXpos(x)
{
    var lgX = ((lmapData.posX + lmapData.spaceing) * x) + lmapData.areaPosX + lmapData.correctionX; 
    return lgX;
}

//Gets what is the coordinate to be drawn on screen
function GetLargeMapYpos(y)
{
    return (lmapData.posY * y) + lmapData.areaPosY + lmapData.correctionY;
}

//Gets what is the coordinate for current hexagon
function GetWorldXpos(x, y)
{   
    return (x + lmapData.cameraPosX) - ((y + lmapData.cameraPosY) * 2); 
}

//Gets what is the coordinate for current hexagon
function GetWorldYpos(x, y)
{   
    return y + lmapData.cameraPosY + (x + lmapData.cameraPosX);
}

Any suggestions?

share|improve this question
    
I havn't put much thought into it but can't you just use the 60 degrees to an amount of radius as offset? –  Sidar May 18 '13 at 21:47

2 Answers 2

up vote 8 down vote accepted

Before I answer the question you already asked, some notes:

You can use A* with the original grid system you are using. The key things you need are neighbors and distance (for the heuristic). For neighbors with your grid system, you need to do something different for even and odd columns (as you mention); here's how:

neighbors =  [
   [  [+1, +1],  [+1,  0],  [ 0, -1],
      [-1,  0],  [-1, +1],  [ 0, +1]  ],
   [  [+1,  0],  [+1, -1],  [ 0, -1],
      [-1, -1],  [-1,  0],  [ 0, +1]  ]
];
parity =  x & 1;
d = neighbors[parity][direction];
return Hex(x + d[0], y + d[1]);

For distances, the simplest thing to do is to convert from your coordinate system to a 3-axis form and then get distances with that. So that brings us to coordinate conversion.

In my guide to hexes, I call your coordinate system “even-q” because the even columns(q) are pushed down. (Note: in my guide I call hex coordinates q,r to distinguish them from 3-axis coordinates x,y,z but since you use x,y in your code I'll use xx,yy,zz for 3-axis in this answer.) You can convert your x,y to 3-axis xx,yy,zz with this:

xx = x
zz = y - (x + x&1) / 2
yy = -xx-zz

Once you've converted both the source and destination to the xx,yy,zz form, you can get distances easily:

distance = (abs(xx1 - xx2) + abs(yy1 - yy2) + abs(zz1 - zz2)) / 2

To answer your actual question, once you have the xx,yy,zz form, take a look at the 3-axis system below; I think -zz,-yy will give you what you want:

3-axis hex coordinate diagram

share|improve this answer
    
Thanks for the answer! A bit late right now for me, see if I can give it a shot tomorrow. –  Jonas Lindahl May 18 '13 at 23:05
    
+1; I came into this question with the intention of posting a link to your guide. You've saved me the trouble! (Except that I've done it anyway, now.) –  Trevor Powell May 20 '13 at 3:02

Consider this:

public sealed partial class HexCoords {
  static HexCoords() {
    MatrixUserToCanon  = new IntMatrix2D(2, 1,  0,2,  0,0,  2);
    MatrixCanonToUser  = new IntMatrix2D(2,-1,  0,2,  0,1,  2);
  }
  protected HexCoords(CoordsType coordsType, IntVector2D vector) {
    switch(coordsType) {
      default:
      case CoordsType.Canon: _vectorCanon = vector;  isUserNull  = true;  break;
      case CoordsType.User:  _vectorUser  = vector;  isCanonNull = true;  break;
    }
  }
  protected static IntMatrix2D MatrixUserToCanon;
  protected IntVector2D VectorCanon {
    get { return !isCanonNull ? _vectorCanon : VectorUser * MatrixUserToCanon; }
    set { _vectorCanon = value;  isUserNull = true; }
  } IntVector2D _vectorCanon;
  bool isCanonNull;

  protected static IntMatrix2D MatrixCanonToUser;
  protected IntVector2D VectorUser {
    get { return !isUserNull  ? _vectorUser : VectorCanon  * MatrixCanonToUser; }
    set { _vectorUser  = value;  isCanonNull = true; }
  } IntVector2D _vectorUser;
  bool isUserNull;
}

with these utility classes:

public struct IntMatrix2D : IEquatable<IntMatrix2D> {
  public static IntVector2D operator * (IntVector2D v, IntMatrix2D m) {
    return new IntVector2D (
      v.X*m.M11 + v.Y*m.M21 + m.M31,  v.X*m.M12 + v.Y*m.M22 + m.M32,  v.W*m.M33
    ).Normalize();
  }
}

public struct IntVector2D : IEquatable<IntVector2D> {
  public int X { get; private set; }
  public int Y { get; private set; }
  public int W { get; private set; }
  public IntVector2D(int x, int y, int w) : this() {
    X = x;      Y = y;      W = w;
  }
  public IntVector2D Normalize() {
    var x = (X >= 0) ? X : X - W;
    var y = (Y >= 0) ? Y : Y - W;
    return new IntVector2D(x/W, y/W);
  }
}

For this purpose canonical coordinates have origin in the upper-left, with y-axis running vertically downwards and x-axis running at 120 degrees up and to the right. User coordinates are from the same origin, with y-axis running vertically downwards and x-axis running horizontally to the right, zigging down and then up from the origin.

I have implemented this and assorted other utilities for Hex-Grid manipulation in an open source library (MIT Licence) here on CodePlex.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.