itch.io is community of indie game creators and players

Devlogs

Making a hex grid in Puzzlescript

Hex Pivot
A browser game made in HTML5

Puzzlescript, the system for making web-friendly sokoban games, is designed around a square grid. So, how does a hex grid work? A couple of playtesters were curious, so I wanted to write about my approach. Please note that the code snippets are partial and don't include all the rules and sections, just some core pieces.

Requirements

My game Hex Pivot was made in Puzzlescript Next, whose documentation says the fundamental features we need (tags, mappings, and transformations) come from Pattern:Script. Mappings define ordered relationships between sprite tags, and transformations allow us to shift where each sprite is displayed on the screen. Puzzlescript Next's subroutines also came in handy for some oft-repeated rules.

Columns

My starting point was to define alternating columns, and then assign sprites to columns as a late rule. Essentially, we want the center of the tile hexagon to be the center of the square grid in column A, and then simply shift the tiles halfway up in column B. Note that the tile is larger than the normal sprite size. Also, if you use too much of the sprite space, it may layer or occlude in ways that don't look great.

sprite_size 10
====
TAGS
====
Columns = A B
=======
OBJECTS
=======
Col:A
black
Col:B
blue
Tile:A translate:left:3 translate:down:1
white
................
.....000000.....
....0......0....
...0........0...
..0..........0..
.0............0.
..0..........0..
...0........0...
....0......0....
.....000000.....
................
Tile:B copy:Tile:A translate:up:5
white
=====
RULES
=====
[ late Tile:A Col:B ] -> [ Tile:B Col:B ]

Bursting

The next big challenge was recreating basic sokoban rules on this grid: movement and pushing. To do that, I made a mapping of the hexagonal directions we cared about, and then set up a subroutine to burst a ring of temporary objects around any starting point, each tagged with their hex direction. Note that the burst acts differently depending on which column it starts in.

====
TAGS
====
HexDirs = NW N NE SE S SW
========
MAPPINGS
========
HexDirs => HexCW
NW N NE SE S SW -> N NE SE S SW NW
HexDirs => HexCCW
NW N NE SE S SW -> SW NW N NE SE S
=====
RULES
=====
subroutine Make Burst
[ Burst ] -> [ ]
[ BurstCenter ] -> [ ]
[ BurstStart ] -> [ BurstStart BurstCenter ]
down [ BurstStart Col:A | ] -> [ BurstStart Col:A | BurstTemp ]
up [ BurstStart Col:B | ] -> [ BurstStart Col:B | BurstTemp ]
up [ BurstStart | ] -> [ BurstStart | Burst:N ]
down [ BurstStart | ] -> [ BurstStart | Burst:S ]
left [ BurstStart Col:A | ] -> [ BurstStart Col:A | Burst:NW ]
right [ BurstStart Col:A | ] -> [ BurstStart Col:A | Burst:NE ]
left [ BurstStart Col:B | ] -> [ BurstStart Col:B | Burst:SW ]
right [ BurstStart Col:B | ] -> [ BurstStart Col:B | Burst:SE ]
left [ BurstTemp | ] [ BurstStart Col:A ] -> [ BurstTemp | Burst:SW ] [ BurstStart Col:A ]
right [ BurstTemp | ] [ BurstStart Col:A ] -> [ BurstTemp | Burst:SE ] [ BurstStart Col:A ]
left [ BurstTemp | ] [ BurstStart Col:B ] -> [ BurstTemp | Burst:NW ] [ BurstStart Col:B ]
right [ BurstTemp | ] [ BurstStart Col:B ] -> [ BurstTemp | Burst:NE ] [ BurstStart Col:B ]
[ BurstStart ] -> [ ]
[ BurstTemp ] -> [ ]

(hmmmm the columns in the image might be reversed)

Then I made a player that rotated using the hex directions, and would move to the burst tile corresponding to its rotation and push any crate on that burst tile. The pivoting mechanic in my game, instead of pushing crates, would just move crates further along the ring of burst tiles.

Since adjacency was dependent on columns, the level section of the code looked unusual. The couple times I changed how wide the margins of the level were, I had to go and move characters in every other column either up or down.

Big Rotate

To rotate the entire level at once, I needed a very different solution. During initialization I gave all the relevant tiles three coordinates: their row from center, their column along that row, and which of the 6 sliced sectors it fell in. Then rotating meant just changing which sector the relevant sprites were in.

Despite sounding simple, this exploded the instruction count from 200 to almost 7 thousand, which causes a noticeable pause when the game loads. The more hex rows and columns to consider, the more instructions it generated, so my challenge during design was to shrink the play space and keep normal pivots away from the furthest rows so that crates couldn't get stuck outside the coordinate space.

Leave a comment