This tutorial is the third part of a series about hexagon maps. This time, we'll add support for different elevation levels, and create special transitions between them.

Cell Elevation

We have divided our map into discrete cells, to cover a flat area. Now we'll give each cell its own elevation level as well. We'll use discrete elevation levels, so store it in an integer field in HexCell .

public int elevation;

How high should each successive elevation step be? We could use any value, so let's define it as another HexMetrics constant. I'll use five units per step, which produces very obvious transitions. For an actual game I'd probably use a smaller step size.

public const float elevationStep = 5f;

Editing Cells Up to this point we could only edit the color of a cell, but now we can also change its elevation. So the HexGrid.ColorCell method is no longer sufficient. Also, we might later add even more editable options per cell. This requires a new editing approach. Rename ColorCell to GetCell and have it return the cell at a given position instead of settings its color. As it now no longer changes anything, we should also no longer immediately triangulate the cells. public HexCell GetCell (Vector3 position) { position = transform.InverseTransformPoint(position); HexCoordinates coordinates = HexCoordinates.FromPosition(position); int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2; return cells[index]; } Now it is up to the editor to adjust the cell. After that's done, the grid needs to be triangulated again. Add a public HexGrid.Refresh method to take care of that. public void Refresh () { hexMesh.Triangulate(cells); } Change HexMapEditor so it works with the new methods. Give it a new EditCell method that takes care of all the editing of a cell, followed by refreshing the grid. void HandleInput () { Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(inputRay, out hit)) { EditCell( hexGrid.GetCell(hit.point) ) ; } } void EditCell (HexCell cell) { cell.color = activeColor; hexGrid.Refresh(); } We can adjust elevations by simply assigning a chosen elevation level to the cell we're editing. int activeElevation; void EditCell (HexCell cell) { cell.color = activeColor; cell.elevation = activeElevation; hexGrid.Refresh(); } Just like with colors, we need a method to set the active elevation level, which we'll link to the UI. We'll use a slider to select from an elevation range. As sliders work with floats, our method requires a float parameter. We'll just convert it to an integer. public void SetElevation (float elevation) { activeElevation = (int)elevation; } Add a slider to the canvas via GameObject / Create / Slider and place it underneath the color panel. Make it a vertical slider which goes from bottom to top, so it visually matches elevation levels. Limit it to whole numbers and give it a reasonable range, like from 0 to 6. Then hook its On Value Changed event to the SetElevation method of our Hex Map Editor object. Make sure to select the method from the dynamic list, so it will be invoked with the slider's value. Elevation slider.

Visualizing Elevation When editing a cell, we're now setting both its color and its elevation level. While you can check the inspector to see that elevations indeed change, the triangulation process still ignores it. All we need to do is adjust a cell's vertical local position whenever its elevation changes. To make this convenient, let's make HexCell.elevation private and add a public HexCell.Elevation property. public int Elevation { get { return elevation; } set { elevation = value; } } int elevation; Now we can adjust the cell's vertical position whenever its elevation is edited. set { elevation = value; Vector3 position = transform.localPosition; position.y = value * HexMetrics.elevationStep; transform.localPosition = position; } Of course this requires a small adjustment in HexMapEditor.EditCell . void EditCell (HexCell cell) { cell.color = activeColor; cell. Elevation = activeElevation; hexGrid.Refresh(); } Cells at different heights. Does the mesh collider adjust to match the new elevation? Older versions of Unity required setting the mesh collider to null before assigning the same mesh again. It just assumed that meshes don't change, so only a different mesh – or null – triggered a collider refresh. This is no longer necessary. So our current approach – reassigning the mesh to the collider after triangulating – is sufficient. The cell elevations are now visible, but there are two problems. First, the cell labels disappear below elevated cells. Second, the connections between cells ignore elevation. Let's fix that.

Repositioning Cell Labels Currently, the UI labels of the cells are created and positioned once, and then forgotten. To update their vertical positions, we have to keep track of them. Let's give each HexCell a reference to the RectTransform of its UI label, so it can be updated later. public RectTransform uiRect; Assign them at the end of HexGrid.CreateCell . void CreateCell (int x, int z, int i) { … cell.uiRect = label.rectTransform; } Now we can expand the HexCell.Elevation property to also adjust the position of its cell's UI. Because the hex grid canvas is rotated, the labels have to be moved in the negative Z direction, instead of the positive Y direction. set { elevation = value; Vector3 position = transform.localPosition; position.y = value * HexMetrics.elevationStep; transform.localPosition = position; Vector3 uiPosition = uiRect.localPosition; uiPosition.z = elevation * -HexMetrics.elevationStep; uiRect.localPosition = uiPosition; } Elevated labels.