Grid edges

 from Red Blob Games
15 Jan 2019, updated Jun 2020

Games with grids usually use the tiles but there are also cool things to do with edges and vertices. For construction games the edges can be useful for blocking connections between tiles (walls, chasms, windows) and allowing connections between tiles (doors, pipes, wires).

Let's start with an example: building walls. A common approach to implementing walls is to place thick walls on the tiles. An alternative is to place thin walls on the edges. Click or tap on the tiles or edges to toggle the walls:

Thick walls on tiles
Thin walls on edges

Examples

Sometimes we want both tiles and edges. I try to make one of the two primary and then calculate the other one. In this example showing grid lighting, the tiles are primary. We can light edges when either tile is lit. Click or tap on tiles:

Tiles are primary; edges are secondary (OR)

Consider the example of pipes/wires between adjacent tiles. We want to know which tiles are connected to the pipe network, but we also want to know which edges connect tiles to each other. If we make the tiles primary, we can place edges when both tiles are marked. Click or tap on tiles:

Tiles are primary; edges are secondary (AND)

Alternatively, we can make the edges primary, and place pipes on tiles when any edge is marked. Click or tap on edges:

Edges are primary; tiles are secondary (OR)

Here's an example of adding thin walls when exactly one neighboring tile has a thick wall. Click or tap on tiles:

Tiles are primary; edges are secondary (XOR)

Whenever possible, I make either tiles or edges primary, and calculate the other using a function. I find this less error prone than putting constraints on what combinations of tiles and edges are allowed. The "pipe" style connectors and the "wall" style dividers use the same logic underneath. A pipe is drawn between the tile centers; a wall is drawn between the corners.

Coordinate systems#

Coordinates for square tiles have two different conventions. In math and in 3D graphics, Y increases upwards; in many 2D graphics systems, Y increases downwards. On this page I'm going to show Y increasing downwards.

{{tile.q}}, {{tile.r}}

How about edges? We could label the four sides of each square:

{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},S{{tile.q}},{{tile.r}},W{{tile.q}},{{tile.r}},E

In many situations we want to treat 0,0,S and 0,1,N as the same edge, and similarly 0,0,E and 1,0,W. In these situations we can keep the N and W sides and discard S and E. We could've kept S instead of N, or E instead of W, but I'll work out the details for N and W.

{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},W

Given a tile q,r its four bordering edges are q,r,N ; q,r,W ; q,r+1,N ; q+1,r,W.

{{tile.q}},{{tile.r}}{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r+1}},N{{tile.q}},{{tile.r}},W{{tile.q+1}},{{tile.r}},W

The rules for edges depend on whether it's an N or W edge. Given an edge q,r,N its two joining tiles are q,r-1 ; q,r. Given an edge q,r,W its two joining tiles are q-1,r ; q,r.

{{tile.q}},{{tile.r}}{{tile.q}},{{tile.r}},N{{tile.q}},{{tile.r}},W

For more details, including coordinates for corners and more tile+edge+corner relationships, see my guide to grids.

Pixel lookup#

In some games we want to be able to determine which tile/edge is closest to the mouse position. When working with tiles, we find the closest tile by looking at squares. These squares are exactly the same as the tiles. It's so simple we don't even think about it! Mouse over the map to see the area of pixels that would activate a tile (touch not supported).

When working with tile edges, we find the closest tile by looking squares at a 45° angle. Each square is made from two tile centers and two tile corners. Mouse over the map to see the area of pixels that would activate an edge (touch not supported).

If we want the mouse to work with both tiles and edges, we can build polygons that work with both. The tiles are detected with small squares, and edges are detected with hexagons. Move the slider to control the sizes, and mouse over the map to see what is activated (touch not supported).

Favor tiles ←
→ favor edges

Alternatively, calculate both the mouseover tile and mouseover edge, and pick the one that's closer to the mouse pointer.

Corner pieces#

If you're working with square tiles, edges, and corners all in the same system, there's a clever trick of using the low bit of x and the low bit of y: 00 = corner, 01 = west edge, 10 = north edge, 11 = tile. Let's see how this works. Move the slider to see how this data is stored in an array.

{{row-1}}{{col-1}}{{corner.q}},{{corner.r}},NW{{tile.q}},{{tile.r}}{{edge.q}},{{edge.r}},{{edge.s}}
Render like this ←
→ store in array
  • Store  corners  q,r,NW in array[2*q][2*r]
  • Store  N edges  q,r,N in array[2*q+1][2*r]
  • Store  W edges  q,r,W in array[2*q][2*r+1]
  • Store  tiles  q,r in array[2*q+1][2*r+1]

This approach lets you store corners, edges, and tiles all in the same array. I haven't had a use for this in my own projects but I'm including it because it's a neat idea that someone might find useful. Anna Harren has a way to do this with hexagons too![1]

More#

Some games, such as Oxygen Not Included and Factory Idle, seem to have a hybrid that keeps track of both tile and edge connections. Some games, such as Factorio and Production Line, have directions along the edges; see my page about conveyor belts.

Email me , or tweet @redblobgames, or comment: