Superhex

Stadia/EGS Name: str-read (aka str, aka strowk)
Contributors: str-read (aka str, aka strowk)
Package Name: Superhex
Package Description: Package with utilities to work with maps on hexagonal grid
Share link: Crayta

Installation instructions

Drop “SuperhexGrid” template on the world. Make sure that it is not rotated (all rotations are zero).
Drop “SuperhexForPlayer” script folder template on player template.
Place starting location somewhere on top of the grid.

To make mouse pointer work:

  • Enable orbit camera on player, increase camera distance
  • Add TrackMouse package
  • Drop TrackMouseForPlayer template on player template

Also for demo/debug purposes you can add DebugSuperhex script on Player to enable mouse pointer hover over hexes and make apple follower to go to hex, on which you have clicked.

Images/Video

Pathfind:


Note that package does not include functionality to define passable hexes, this could be done by custom code with manual neighbors function.

Video demostrating hex colors onHover and Pathfind:

Credits

  • Core algos are mostly based on this guide: Hexagonal Grids
  • Daigoro’s “Screen Functionset” inspired implementation of projection from screen pointer to world ( but had to change a lot, so could not directly link package )
  • TrackMouse is another my package, here only used to track mouse based on orbit camera (without requires cursor widget)
  • priority queue from here: Priority Queue implemented in lua, based on a binary heap. · GitHub

Details

Package provides hexagonal grid as a template, which is composed of two items: screen, which is able to render hexes and ground, which is required to be able to point on a hex with mouse (using TrackMouse package and Raycast function).

Limitations

SuperhexGridWidget is supposed to always be a square (have same width and height).
It also should not be rotated, its x axis should correspond to x in-word axis and same for y. The easiest way to make sure of it is to keep no rotations on locator in the root of SuperhexGrid template instance.

SuperhexGrid script on the grid template must have size property matching size (width/height) configured for SuperhexGridWidget. By default it is 5000.

Grid might need some time to initialize (specifically to retrieve window sizes). Once that is done - SuperhexGrid ready property will switch to true and onReady even will be triggered. Event is triggered on both server and client.

API Reference

SuperhexGrid is a script on template of the same name.
This script allows you to control how hexes are displayed and provides some
in-world to hex coordinates transformations as well.

SuperhexUtils is a collection of useful utilities, mostly based on this guide: Hexagonal Grids
This script is attached to grid as well as to player (if you dropped SuperhexForPlayer template on your player).

SuperhexUIScript handles UI. Currently only used on client with onHover event. This script is normally added on player as a part of SuperhexForPlayer.

SuperhexGrid:CleanHexes()

Will remove all displayed hexes. By default screen is transparent then.

SuperhexGrid:SetHex(hex)

Set single hex to show on the grid. Hex has following fields:

{
	row = 0,
	col = 0,
	color = "#abab33"
}

row, col - Odd-R coordinates (see Coordinates section below).
color - fill color

SuperhexGrid:GetHexCenterWorldPosition(hex)

Receives hex Oddr coordinates. Returns center of hex on in world.

Example:

grid.SuperhexGrid:GetHexCenterWorldPosition({
	row = 0,
	col = 0
})

SuperhexGrid:FromWorldPositionToOddr(hex)

Returns hex oddr coordinates - table with following format:

{
	row = 0,
	col = 0
}

SuperhexUIScript.properties.onHover event

This works only on client.

If you added and initialized TrackMouse package and configured orbit camera, then you will have a pointer attached to your mouse/right-stick input. This pointer by default is an apple. Whenever pointer is hovered above hex (when that hovering changes) - onHover event would be triggered passing Oddr offset coordinates as well as grid entity.

player.SuperhexUIScript.properties.onHover:Listen(self, "HoveredOnHex")

function MyScript:HoveredOnHex(offset, grid) 
-- offset is OddR coordinate of hex, grid.SuperhexGrid would be the grid
-- f.e. example call like this:
	local position = grid.SuperhexGrid:GetHexCenterWorldPosition(offset)
end

SuperhexUtils transformations

Most of these are only used internally for certain algos. Refer to Coordinates below to know more.
You might need to use these for some of methods as well.

  • SuperhexUtils:OddrToCube(offset)
  • SuperhexUtils:CubeToOddr(cube)
  • SuperhexUtils:CubeToAxial(cube)
  • SuperhexUtils:AxialToCube(axial)
  • SuperhexUtils:PixelToPointyHexAxial(point, size)
  • SuperhexUtils:PixelToPointyHexOddr(point, size)
  • SuperhexUtils:GetPointyHexOddrPixelCenter(hex, size)

SuperhexUtils:CubeRing(center, radius)

Returns ring around particular hex.

  • center is a center of ring, should be Cube coordinates of hex
  • radius - radius of the ring, must be more than zero

SuperhexUtils:CubeSpiral(center, radius)

Returns spiral around particular hex.

  • center is a center of spiral, should be Cube coordinates of hex
  • radius - radius of the spiral, must be more than zero

SuperhexUtils:OddrNeighbour(hex, direction)

Returns neighbour of particular hex in specified direction.

  • hex - Oddr coordinates of the hex
  • direction - there are 6 neighbours for each hex in 6 directions, this should be a number between 1 and 6 (inclusive)

SuperhexUtils:OddrNeighbours(hex)

Returns all neighbours of specified hex.

  • hex - Oddr coordinates of the hex

SuperhexUtils:CubeDistance(a, b)

Returns distance between two hexes.

  • a, b - Cube coordinates of the hexes

SuperhexUtils:RangeCube(center, N)

Returns all hexes within certain range of the center.
This returns table with Vectors, which are Cube coordinates of hexes.

  • center - Cube coordinates of the center hex
  • N - radius of range

SuperhexUtils:IntersectOddrRanges(center1, N1, center2, N2)

Returns all hexes which are in both ranges.
This returns table with Oddr coordinates of hexes.

  • center1 - Oddr coordinates of the center hex of first range
  • N1 - radius of first range
  • center2 - Oddr coordinates of the center hex of second range
  • N2 - radius of second range

SuperhexUtils:IsInsideRange(hex, center, N)

Returns boolean whether or not specified hex is inside specified range.

  • hex - Oddr coordinates of hex
  • center - Oddr coordinates of the center hex
  • N - radius of range

SuperhexUtils:Pathfind(from, to, neighbours)

This implements A* algo. It receives neighbours function, which you can use to add obstacles to your map and limit its size. If function was not passed, default function would be used OddrNeighbours. If path could not be found - empty table would be returned. Otherwise - table with hexes Oddr coordinates.

  • from - Oddr coordinates of starting hex
  • to - Oddr coordinates of the target hex
  • neighbours - function returning table with neigbours of passed hex (Oddr)

Coordinates

Hex Coordinates

There are three types of low-level “hex” coordinates used in the package: Oddr, Cube and Axial.

Oddr is also known as offset. Offset coordinates describe hex position in terms of rows and columns. By default most of the time this coordinate is used for hex. See for example description of SuperhexGrid:SetHex above. There are four different ways to express offset-type coordinates. OddR is one of those four. Read more about that here - Hexagonal Grids .
Note that only OddR is used in Superhex from available offset coordinates.
Oddr coordinates are implemented as a table with two fields: row and col:

{
	row = 0,
	col = 0
}

Cube is used for some algos in SuperhexUtils. Vector Crayta primitive is used to store this.

Axial is a subform of Cube, for storing this Vector2D Crayta primitive is used.

Most of the time user-facing API is using OddR. You only might need Cube or Axial if you want to implement something more complex or use low level functions directly for some reason.

Other coordinates

In several functions there are two other types of coordinates: Pixel and InWorld.

InWorld is usual Vector representing position in the world of standard Crayta entities, etc.

Pixel is a coordinate of pixel inside of SuperhexGridWidget. This is needed as an intermediate to transform between InWorld to OddR and back. Normally end-users do not need this.

Known issues

  • pointer is a bit unlinearly moving close to edges. There is a distortion due to not ideal screen to world coordinates transfromation. It would be fixed if this feature is implemented: Inverted ProjectPositionToScreen
  • pathfind function is limited by 1 second lua scripts execution and it is not a problem when there are no obstacles, but if neighbors function have to do raycasts to define whether hex is passable - this could easily hit limit. This could be avoided by either cutting execution once it done certain amount of iterations (and returning path, which is heurisically better) or/and splitting calculations into several ticks. Latter would require API change as well as serious algo rewrite.
  • SuperhexGrid:SetHexes currently only optimizes sending over network (breaks to batches and sends each batch separtely). Time to modify collection of hexes in widget will grow with number of already set hexes