Click or drag to resize

Amit Patel's Guide for Hex Grids and Gamelogic Grids

If you have done any work with hex grids, the chances are good that you came across the best guide for the mathematics and algorithms for hex grids: Hexagonal Grids by Amit Patel. This is how you can do the various things described in that guide with Grids.

Angles, Size and Spacing

For the most part, if you use Grids, you don’t have to worry about these low-level details. Conversion to world space is handled by maps.

Coordinate Systems

Grids only work with one coordinate system: axial coordinates. In the very early days of Grids (before it was for sale), we used offset coordinates. As we made more examples and implemented more algorithms, we found that the offset coordinate system is very clumsy, so we changed it to use axial coordinates instead. The axial coordinate system works like a (integer) vector space, and many algorithms are much simpler compared their implementations using offset coordinates. In particular, it makes sense to add two points together to get a third (where the second point is interpreted as an offset).

There are two cases where offset coordinates can be useful:

  • For display (on certain boards, the coordinates may be more compact and attractive, since they don’t have to have negative values, while still having a corner as the origin).

  • For certain algorithms (if your game uses for instance rows and wiggly columns)

In these two cases, you will have to implement a conversion, something which is straightforward to do. (It's a good idea to implement this conversion as a IMapTInput, TOutput.)

Neighbors

There are two ways to access the neighbors of a point in a hex grid. To get all the neighbors of the point (regardless of whether they lie in some grid or not), use

var neighbors = PointyHexPoint.GetOrthogonalNeighbors(point);

To get only the neighbors inside a specific grid (or shape), use:

var neighborsInGrid = PointyHexPoint.GetOrthogonalNeighbors(point).In(grid);

Hex grids also have constants defined for each direction a neighbor can be in: NorthWest etc. These can be added to a point to get the neighbor:

var northWestNeighbor = point + PointyHexPoint.NorthWest;

Grids 2 also supports more general ideas of "neighbor", so that you can also consider two cells "neighbors" if they are next to each other in the various ways hex-chess pieces can move, for example (see Hex Chess

Diagonals

Grids does not support diagonals directly, but it is easy to define your own constants for diagonal directions, for example:

readonly PointyHexPoint North = PointyHexPoint.NorthWest + PointyHexPoint.NorthEast;

These can be added to a point to get the neighbor:

var northWestNeighbor = point + PointyHexPoint.NorthWest;

And if you add these to a list, you could use that with the GetVectorNeighbors(IEnumerableGridPoint2) method.

Distances

To get the hex-distance between two grid points, you use the following:

int distance = PointyHexPoint.HexNorm(p1 - p2);

But grids also supports several other notions of distance for hex points:

Line drawing

There are two basic ways to work with "lines". You can use the very general "line as a way to get the next point from the current point" model, that is described in this topic: Lines. Or you can work with normal Vector2 lines, and use a map to get the grid points.

Coordinate Ranges

To get all the points within a certain distance from a give point, you can use a simple LINQ query:

var pointsInRange = grid.Points.Where(p => PointyHexPoint.HexNorm(p - center) <= range);

For intersections, you can chain queries:

var pointsInRange = grid.Points
    .Where(p => PointyHexPoint.HexNorm(p - center1) <= range1);
    .Where(p => PointyHexPoint.HexNorm(p - center2) <= range2);

There are two complications which you may face in your game:

  • to use different distance metrics (including weighted costs)

  • to take obstacles into account

For this, we provide additional distance metrics (see above), and the methods GetPointsInRangeTPoint(IGridTPoint, TPoint, FuncTPoint, IEnumerableTPoint, FuncTPoint, Boolean, FuncTPoint, TPoint, Int32, Int32).

Rotation

There are several rotation functions defined in the static class PointyHexPoint such as Rotate60(GridPoint2).

In the Algorithms class there is also a method for transforming (including rotating) a list of points TransformShapeTPoint(IEnumerableTPoint, FuncTPoint, TPoint). There is a method ReverseSelectTPoint(IImplicitShapeTPoint, FuncTPoint, TPoint) that can be used on shapes and grids to get a new transformed shape.

Grids 2 also have both integer and floating point matrices (2 by 2 and 3 by 3) that allows you to do general linear transformations. See for example Matrixf22 and Matrixf33.

Rings

You get rings of various shapes by doing suitable LINQ queries using the various norms. Below are a few examples

Hexagonal Ring
var ring = grid.Points
    .Where(p => PointyHexPoint.HexNorm(p - center) == radius);
Star Ring
var ring = grid.Points
    .Where(p => PointyHexPoint.StarNorm(p - center) == radius);
Star Ring
var ring = grid.Points
    .Where(p => radius1 < PointyHexPoint.HexNorm(p - center))
    .Where(p => PointyHexPoint.HexNorm(p - center) < radius2);
Spirals

You can get a spiral of points from a grid using

var spiral = PointyHexPoint.GetSpiralIterator(grid, center, ringCount);
Field of View

Hex to Pixel and Pixel to Hex

The conversion is done with maps. There are two maps involved: a space map does the basic "skewing", and a "round map" takes the vector and turns it into a grid point. See Space Maps.

Rounding to nearest hex

To round a vector3 to a the nearest grid point in a hex grid, you can use a round map. To get a round map, use HexRound

Map Storage (Grid Data Storage)

(Note, this "Map" is used in a different sense than we use map. Here it refers to grid data, which is typically a game "map".)

The beauty of Grids is that you need not worry about storage at all, everything is taken care of behind the scenes. This is really convenient when making more complicated shapes using the grid builder functionality. Once the grid is created, you can access data like you would from a dictionary, using grid points as keys. You do not need to know how it is stored in the background.

In special cases, you may want to implement your own storage structure, for example, if you have a grid that can grow dynamically. In such cases, you can let your implementation implement the IGrid interface, and still have all our algorithms work on your grid, regardless of how you handle storage.