StormWorksWeb & Game Design
May 10, 2016 9:30 pm

Today I’m going to attempt to create a 2d hexagonal grid in Unity, going through the steps required in depth and explaining the code involved (for my sake more than anyone else!) Whenever you see a link, click it to visualise the process, or change between pointy topped and flat topped hexagons

Why Hexagons?

Many games use square grids due to how easy they can be to create and manage, but they do have a number of shortcomings. Namely, the diagonal distance between two squares on a grid is different from the horizontal and vertical distances. You either have to code especially for these circumstances or disallow diagonal movement in your game entirely. Hexagon grids are more difficult to initially set up, but once you’ve overcome this hurdle your movement code is a lot more elegant. Plus I think hex grids have a certain charm that square tiles cannot compete with.

Understanding Hexagons

Now it’s time to learn more about hexagons than you ever really knew you wanted. In a 2d grid, which is what we’re after, you can orient your hexagons one of two ways: Flat topped or Pointy topped. It’s mostly down to personal preference which one you use, but the algorithm used to calculate the distance between adjacent hexes is different depending on their orientation. I’m going to go with flat topped ones, but for the sake of convenience I’ll walk through how to do it with either.

To start with, you need to figure out how big you want your hexagons to be. The easiest way to do this is to imagine the hexagon as 6 equilateral triangles. The size of any edge (x) is the same as the radius of the outer circle (purple).

There is also an inner circle (aquamarine), which is important as its radius allows you to find the distance from the centre of one hexagon to the centre of a neighbouring one. The radius of this circle is equal to the height of one of the equilateral triangles, which we can work out using the Pythagorean theorem – Visualise this.

So putting the values into the equation, where the radius we’re looking for is r, we get this:

r2 + (x/2)2 = x2

We know the values in purple, so to find out the value of r, we use the following calculation:

r = √x2(x/2)2, so for example, if x is 1, r = 0.866025403784. This number is important, as regardless of the size of your hexagons, their height is always 0.866025403784 times the width.

My First Unity Hex Grid – The Theory

Unlike with a square grid, hexagons don’t exactly neatly fit together, you have to offset every second row to slot the pieces into position. – Visualise this. This is where all the values we figured out in the first step come into play.

For flat topped hexagonal grids, the offset is for every other vertical column in the grid. Every hexagon is moved down by r and left by x/2.

For pointy topped hexagonal grids, the offset is for every other horizontal row in the grid. Every hexagon is moved up by x/2 and right by r.

My First Hexagonal Grid in Unity – The Code

Now it’s time to actually get something in Unity. I’ve created a hexagon sprite that you can use for this, it’s a lovely shade of green. So open up Unity, import the hexagon and start up a new C# script:

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 using UnityEngine; using System.Collections;   public class HexGrid : MonoBehaviour {   public GameObject Hex; // Grid width in hexes public int gridWidth = 5; public int gridHeight = 5;   private float hexWidth; private float hexHeight;   // Use this for initialization void Start () {   }   // Update is called once per frame void Update () {   } }```

Save the script here and attach it to the camera. Then create a prefab out of the hex sprite and drag it into the hex gameobject variable to link them. Next, dive back into your code editor and create a new function:

```1 2 3 4 void setHexSizes() { hexWidth = Hex.GetComponent<Renderer>().bounds.size.x; hexHeight = Hex.GetComponent<Renderer>().bounds.size.y; }```

This gets the width and height directly from the prefab, so it doesn’t matter if you need to change its dimensions in the future, or even if you plan to use a different sprite with a worse colour. Next we’ll create a function that finds the start point for our first hexagon based on the size of the grid:

```1 2 3 4 5 6 7 8 9 10 Vector3 calcInitialPos() { Vector3 initialPos; initialPos = new Vector3( -hexWidth * gridWidth / 2f + hexWidth / 2, gridHeight / 2f * hexHeight / 2, 0 ); return initialPos; }```

As we start from the origin (0, 0), we divide the grid width and height by 2f to get the right offset. Now, time to test and see if this works with a little temporary function:

```1 2 3 4 5 6 7 8 9 10 11 void createHex(Vector3 pos) { GameObject thisHex = (GameObject)Instantiate(Hex); thisHex.transform.position = pos; } void Start() { setHexSizes(); Vector3 initPos = calcInitialPos(); createHex(initPos); }```

Voila! We have a hexagon!

Now it’s time to make the whole grid! But first we have to create a method that will take a specific grid coordinate, say (2, 1) and translate that into our Unity coordinates, using our initial position as a starting point:

```1 2 3 4 5 6 7 8 public Vector3 calcUnityCoord(Vector2 gridPos) { //Position of the first tile Vector3 initPos = calcInitialPos(); float x = initPos.x + gridPos.x * hexWidth; float y = initPos.y - gridPos.y * hexHeight; return new Vector3(x, y, 0); }```

The reason we’re splitting this into two methods is to make the later offset easier, as the calcUintyCoord can handle any adjustments while keeping the grid position the same. Now we just loop though all available grid positions, (0,0) to (5,5) and pass the co ordinates to our nifty little function:

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void createHexGrid() { GameObject hexGridObject = new GameObject("HexGrid"); hexGridObject.transform.parent = this.transform;   for(int y = 0; y < gridHeight; y++) { for(int x = 0; x < gridWidth; x++) { GameObject thisHex = (GameObject)Instantiate(Hex); Vector2 gridPos = new Vector2(x, y); thisHex.transform.position = calcWorldCoord(gridPos); thisHex.transform.parent = hexGridObject.transform; } } }```

And we have exactly what we expected from the theory!

Now we just need to add the offset. If you remember (It feels like ages ago we figured out the theory!), for pointy topped hexagons, they move r to the right, where r is the radius of the inner circle / half the hexagon width and up by x/2, where x is the radius of the outer circle / half the hexagon height.

If we just move ever second row, however, there will still be gaps in the grid. So to avoid it we can use this nifty trick:

```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Vector3 calcWorldCoord(Vector2 gridPos) { //Position of the first tile Vector3 initPos = calcInitialPos(); float xoffset = 0; float yoffset = 0; // If the row number is a multiple of 2 if (gridPos.y % 2 != 0) // The x offset is equal to the radius of the inner circle, or half the width for pointy hexes xoffset = hexWidth / 2;   float x = initPos.x + xoffset + gridPos.x * hexWidth; // Every new line is offset in y direction by 3/4 of the outer circle diameter, or hexagon height yoffset = 0.75f; float y = initPos.y - gridPos.y * hexHeight * yoffset; return new Vector3(x, y, 0); } //Add it into your start function: void Start() { setHexSizes(); createHexGrid(); }```

And it works!