«

»

Apr 10

Creating a Hex Grid for HTML5 Games in Javascript

In my last post, I described how to do some of the calculations for determining the required measurements to build a hexagon. Today I’ll show you how to take that a step further and build a hex grid or game-field out of those hexagons.

Let’s start with the overview and pseudo-code for populating our grid of hexes.

Overview/Pseudocode:

  1. Populate each row while we have vertical space on the canvas
  2. After each hex (column), increment the x direction by the width of the hex + the side length until we run out of horizontal space on the canvas
  3. Every other row must be offset in the x direction by the width of the hex minus the side length divided by two
  4. After each row increment the y direction by 1/2 the height of the hex

Simple version of JavaScript code:

HT.Grid = function(/*double*/ width, /*double*/ height) {

this.Hexes = [];

var row = 0;
var y = 0.0;
while (y + HT.Hexagon.Static.HEIGHT <= height)
{
   var col = 0;
   var offset = 0.0;
   if (row % 2 == 1)
   {
      offset = (HT.Hexagon.Static.WIDTH - HT.Hexagon.Static.SIDE)/2 + HT.Hexagon.Static.SIDE;
      col = 1;
   }
   
   var x = offset;
   while (x + HT.Hexagon.Static.WIDTH <= width)
   {
      //first parameter of the hexagon constructor is the hexId
      //   which we'll discuss more later
      var h = new HT.Hexagon(null, x, y); 
      this.Hexes.push(h);

      col+=2;
      x += HT.Hexagon.Static.WIDTH + HT.Hexagon.Static.SIDE;
   }
   row++;
   y += HT.Hexagon.Static.HEIGHT / 2;
}

};

That is pretty straight-forward, but before we have a usable grid we may want to add a few more things. If you notice my Hexagon constructor has an "Id" member, the purpose of the Id is to give each hex a unique string identifier, this makes it easy to reference each hex, either programmatically, or for debugging. I like to give my hexes a Letter prefix based on the row plus a number based on the column, but you can do whatever works for you.

Here is my simple function for getting the hex identifiers I want:


HT.Grid.Static = {Letters:'ABCDEFGHIJKLMNOPQRSTUVWXYZ'};

HT.Grid.prototype.GetHexId = function(row, col) {
	var letterIndex = row;
	var letters = "";
	while(letterIndex > 25)
	{
		letters = HT.Grid.Static.Letters[letterIndex%26] + letters;
		letterIndex -= 26;
	}
		
	return HT.Grid.Static.Letters[letterIndex] + letters + (col + 1);
};

Hex Distance:

Finally, since it is common in games to need to figure out the distance between two hexes, we probably want a function that takes two hexagons and returns a distance. To facilitate this, we'll need to assign some coordinates to each of our hexagons that will make it faster and easier to calculate distances when necessary. James McNeill has a very good post on his PlayTechs blog about hex grids where he describes a coordinate system for hex grids, and how to use that coordinate system to find distances between hexes.
Basically, instead of a coordinate system based on the rows and columns, we have columns (shown in green), and diagonal rows (shown in red). Once we have assigned coordinates to our hexagons as shown, we can use the following equation to calculate the distances between two hexes:
\frac{|\Delta x| + |\Delta y| + |\Delta x - \Delta y|}{2}

Here is my JavaScript function that returns the distance between two hexes in the grid:

HT.Grid.prototype.GetHexDistance = function(/*Hexagon*/ h1, /*Hexagon*/ h2) {
	var deltaX = h1.PathCoOrdX - h2.PathCoOrdX;
	var deltaY = h1.PathCoOrdY - h2.PathCoOrdY;
	return ((Math.abs(deltaX) + Math.abs(deltaY) + Math.abs(deltaX - deltaY)) / 2);
};

Note: I'm using the PathCoOrdX and PathCoOrdY members of my Hexagons in the above function, which I populate in my full version of the Grid constructor, but I won't go into how I do that in this post, basically the PathCoOrdX comes from the column variable, and PathCoOrdY is populated in a second pass. You can download the full Grid.js code to see the exact method I use.

The Fun Part:

I've put together a simple form and canvas, similar to what I did in my last post, so that you can see the Hex Grid in action, and play with the parameters. I've also added another option that allows you to switch the hex orientation that you can play with.



Orientation:

Hex Based on Side Length

Side Length (z):

Ratio of Width:Height (4:3, r = 1.333), (2/√3, r = 1.1547...)

Hex Based on Width and Height

Width:

Height:

Source Files

Grid.js - The full source code for the grid class, or you can download all code for these Hexagon articles in this GitHub repo: HexagonTools.

  • mpalmerlee

    There was a bug with how the coordinates are assigned for the rotated hex grid.  It is fixed now and the new version has been uploaded.

    • I tried the GitHub version and the samples didn’t work until I added the following line to the header…
      HT= {};

      • mpalmerlee

        Chad:
        Thanks for bringing that to my attention, I found the problem was I put that new grid prototype function in the main hexagon tools file. I’ve pushed a fix to github.

  • mpalmerlee

    There was a bug with how the coordinates are assigned for the rotated hex grid.  It is fixed now and the new version has been uploaded.

  • Poubelle

    very nice and helpful.

  • Carl Olsen

    have you done any pathfinding or line of sight calculations with this system?

    • mpalmerlee

      No, none of my games that used this grid have called for that.  Sounds like it would be a great addition to the library though!  I could put this up on GitHub if I get enough interest to make it easier for other people to contribute back to the library for things like that.

    • mpalmerlee

      Carl,

      In case you are interested in making changes to the library, I’ve added the code to GitHub:

      https://github.com/mpalmerlee/HexagonTools

      Just submit a pull request if you found time to add any pathfinding or LOS calculations to the code.

  • Kalle

    an anonymous mathematican (who is thankful for your grid-source) remarks that the “distance function” you defined is actually the same as
    Math.max(Math.abs(deltaX),Math.abs(deltaY))
    Cheers 🙂

    • Kalle

       sorry for my previous post, I was wrong :/

  • eugenioclrc perez

    Great code!!!! awesome

  • Harikrishna

    Grate Code. exactly suited for my requirement…. for my game I need to insert images on hexagons.. I tried to get Hexagons by Id’s but I failed.How do I get a particular Hexagon using Id .. Can you explain a little more.. Thanks..

    • Guest

      @mpalmerlee:disqus please help..

    • mpalmerlee

      Thanks for the interest in the library Harikrishna, you could draw an image directly in the hexagon by updating the code within the HT.Hexagon.prototype.draw function. You would just need to draw an image using the HTML5 canvas drawImage method:

      var image = new Image();
      image.onload = function() {
      ctx.drawImage(image, x, y);
      };
      image.src = “path/to/image.png”;

      • Harikrishna

        Thanks for replying mpalmerlee… But I need to place Images dynamically and is based on tabular data I got from server… and even I need to know the adjacent Hexagons for each hexa.. how can i accomplish that part… just tell me the way and i’ll get my hands dirty..

        • mpalmerlee

          I don’t have a method in the Grid to return adjacent hexagons, but it should be pretty straightforward to implement. You might try using the ids to get adjacent hexagons, if for example you look at Hex ID: “F4” the adjacent hexes are D4, E5, G5, H4, G3, and E3. Then you can use the Grid.GetHexById function to get those hex objects.

          • Harikrishna

            Thanks you’re the best 🙂

      • Harikrishna

        image.onload function is not working… you can directly place using ctx.drawImage() method.

  • boingy

    HexagonTools really hit the spot. A thousand thanks!

    Here are a couple of functions I added so I could find the closest hex to a click/touch location. This is useful around the edges of a rectangular map where there are “voids” between the whole hexes, particularly on mobile platforms where touch events may be a bit inaccurate. The functions are given freely and without licence so it’s OK to edit, discard, deride, steal, butcher or ignore the functions as you see fit. Add ’em to the library if you think they are useful.

    /**
    * Returns absolute distance in pixels from the mid point of this hex to the given point
    * @this {HT.Hexagon}
    * @param {HT.Point} p the test point
    * @return {number} the distance in pixels
    */
    HT.Hexagon.prototype.distanceFromMidPoint = function(/*Point*/ p) {
    // Pythagoras’ Theorem: Square of hypotenuse = sum of squares of other two sides
    var deltaX = this.MidPoint.X – p.X;
    var deltaY = this.MidPoint.Y – p.Y;

    // squaring so don’t need to worry about square-rooting a negative number
    return Math.sqrt( (deltaX * deltaX) + (deltaY * deltaY) );
    };

    /**
    * Returns the nearest hex to a given point
    * @this {HT.Grid}
    * @param {HT.Point} p the test point
    * @return {HT.Hexagon}
    */
    HT.Grid.prototype.GetNearestHex = function(/*Point*/ p) {

    var distance;
    var minDistance = Number.MAX_VALUE;
    var hx = null;

    // iterate through each hex in the grid
    for (var h in this.Hexes)
    {
    distance = this.Hexes[h].distanceFromMidPoint(p);

    if (distance < minDistance) // if this is the nearest thus far
    {
    minDistance = distance;
    hx = this.Hexes[h];
    }
    }

    return hx;
    };

    There is scope for a couple of optimisations for very large grids:

    1. Assuming you don't care about real distances, remove the call to Math.sqrt() and work with the square of the distance. Same result, less cycles.

    2. Rather than iterating through the grid from the start you could dive into the middle and do a sort of binary search. The grid would need to record its width/height in hexes when it is created so you could jump over rows/columns. You might even be able to narrow it down directly to 4 (or 2?) hexes with a bit of thought.

    In my application it is fast enough as it is, even on a rubbish mobile phone, so I'm in no hurry to optimise it.
    Have fun, and thanks again.

    Ian

    • mpalmerlee

      Thanks for the code Ian. I think that would be useful to integrate to the library. I’ll take a look and update the code on github.

    • mpalmerlee

      I’d like to give you credit for the functions in the code, right now I have “Ian, (disqus user boingy)” let me know if you want me to put something else.

  • Cris Fuhrman

    Glad this was done for HTML 5 and canvas. Everyone curious about hex-based calculations should check out the site by Amit Patel http://www.redblobgames.com/grids/hexagons/

  • Firstly sorry for bringing up an old thread!

    I’ve had a look at this, and had a little bit of a play if I use the HexCalcs.js file, and add some additional code in the following function:

    function drawHexGrid(){

    var grid = new HT.Grid(800, 600);

    var canvas = document.getElementById(“hexCanvas”);

    //my added bit:

    canvas.addEventListener(“onclick”, function(evt) {

    var Hexa = new HT.Hexagon(null, null, null);

    alert(Hexa.isInBounds(evt.clientX, evt.clientY));

    },false);

    var ctx = canvas.getContext(‘2d’);

    ctx.clearRect(0, 0, 800, 600);

    for(var h in grid.Hexes) { grid.Hexes[h].draw(ctx); }}

    The alert always returns false?

    Am I missing something?

    Cheers!

    • mpalmerlee

      Sorry I just saw your question. I’m guessing the problem is that when you build your hex:

      var Hexa = new HT.Hexagon(null, null, null);

      You should specify an id, x and y coordinates:

      var Hexa = new HT.Hexagon(“myhex”, 10, 10);

      See the function:
      addHexToCanvasAndDraw(x, y) from this file:
      https://github.com/mpalmerlee/HexagonTools/blob/master/js/HexCalcs.js

      Hope this makes sense.