StormWorksWeb & Game Design

Unity hex grids 1: Creating your first hex grid

May 10, 2016 9:30 pm Published by

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.

hexagon

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

Flat topped or Pointy topped

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!

First Hexagon View

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!

Full Hexagon grid in Unity

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!

Hexagon Grid in Unity with offset

If you find anything wrong with the code or just want to chat, please leave a comment below!

Next time I’ll be making different terrain types and dynamically adding them into the createGrid() function.

Leave a Reply

Your email address will not be published.