Created
June 13, 2025 16:12
-
-
Save wallstop/dc277fc6823e75ff4a39c647103c3702 to your computer and use it in GitHub Desktop.
Hex - GridOffset
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| namespace Core.Models | |
| { | |
| using System; | |
| using System.Collections.Generic; | |
| using System.ComponentModel; | |
| using DataStructure.Adapters; | |
| using UnityEngine; | |
| public sealed class GridOffset | |
| { | |
| public const int NeighborCount = 6; | |
| public enum GridDirection | |
| { | |
| UpRight, | |
| Up, | |
| UpLeft, | |
| DownLeft, | |
| Down, | |
| DownRight, | |
| } | |
| public static FastVector2Int GetNeighboringGridCellInDirection(FastVector2Int cell, Vector2 direction) | |
| { | |
| GridDirection gridDirection = direction.AsDirection() switch | |
| { | |
| Direction.North => GridDirection.Up, | |
| Direction.NorthEast => GridDirection.UpRight, | |
| Direction.NorthWest => GridDirection.UpLeft, | |
| Direction.South => GridDirection.Down, | |
| Direction.SouthEast => GridDirection.DownRight, | |
| Direction.SouthWest => GridDirection.DownLeft, | |
| Direction.East => direction.y >= 0 ? GridDirection.UpRight : GridDirection.DownRight, | |
| Direction.West => direction.y >= 0 ? GridDirection.UpLeft : GridDirection.DownLeft, | |
| _ => GridDirection.Up, | |
| }; | |
| return (cell.y % 2 == 0 ? EvenYOffsetLookup[gridDirection] : OddYOffsetLookup[gridDirection]) + cell; | |
| } | |
| public static FastVector2Int GetNeighboringGridCellInDirection(FastVector2Int cell, Direction direction) | |
| { | |
| if (direction == Direction.East) | |
| { | |
| return (cell.y % 2 == 0 | |
| ? EvenYOffsetLookup[GridDirection.UpRight] | |
| : OddYOffsetLookup[GridDirection.DownRight]) + cell; | |
| } | |
| if (direction == Direction.West) | |
| { | |
| return (cell.y % 2 == 0 | |
| ? EvenYOffsetLookup[GridDirection.UpLeft] | |
| : OddYOffsetLookup[GridDirection.DownLeft]) + cell; | |
| } | |
| GridDirection gridDirection = direction switch | |
| { | |
| Direction.North => GridDirection.Up, | |
| Direction.NorthEast => GridDirection.UpRight, | |
| Direction.NorthWest => GridDirection.UpLeft, | |
| Direction.South => GridDirection.Down, | |
| Direction.SouthEast => GridDirection.DownRight, | |
| Direction.SouthWest => GridDirection.DownLeft, | |
| _ => GridDirection.Up, | |
| }; | |
| return (cell.y % 2 == 0 ? EvenYOffsetLookup[gridDirection] : OddYOffsetLookup[gridDirection]) + cell; | |
| } | |
| public static Vector3Int GetNeighboringGridCellInDirection(Vector3Int cell, GridDirection direction, int range = 1) | |
| { | |
| FastVector2Int offset = (cell.y % 2 == 0 ? EvenYOffsetLookup[direction] : OddYOffsetLookup[direction]); | |
| return new Vector3Int(cell.x + (offset.x * range), cell.y + (offset.y * range), cell.z); | |
| } | |
| public static FastVector3Int GetNeighboringGridCellInDirection(FastVector3Int cell, GridDirection direction, int range = 1) | |
| { | |
| FastVector2Int offset = (cell.y % 2 == 0 ? EvenYOffsetLookup[direction] : OddYOffsetLookup[direction]); | |
| return new FastVector3Int(cell.x + (offset.x * range), cell.y + (offset.y * range), cell.z); | |
| } | |
| /* | |
| Cell position mutations to use to find hex cell neighbors. | |
| */ | |
| public readonly FastVector2Int[] Offsets; | |
| /* | |
| AdditionalUnblocked offsets are hexes linked by the "hex lines" | |
| */ | |
| public readonly Dictionary<FastVector2Int, List<FastVector2Int>> AdditionalUnblockedOffsets; | |
| private GridOffset(FastVector2Int[] offsets, Dictionary<FastVector2Int, List<FastVector2Int>> additionalUnblockedOffsets) | |
| { | |
| Offsets = offsets ?? throw new ArgumentNullException(nameof(offsets)); | |
| AdditionalUnblockedOffsets = additionalUnblockedOffsets ?? throw new ArgumentNullException(nameof(additionalUnblockedOffsets)); | |
| } | |
| public static readonly Dictionary<GridDirection, FastVector2Int> EvenYOffsetLookup = new(NeighborCount) | |
| { | |
| { GridDirection.UpRight, new FastVector2Int(0, 1) }, | |
| { GridDirection.Up, new FastVector2Int(1, 0) }, | |
| { GridDirection.UpLeft, new FastVector2Int(0, -1) }, | |
| { GridDirection.DownLeft, new FastVector2Int(-1, -1) }, | |
| { GridDirection.Down, new FastVector2Int(-1, 0) }, | |
| { GridDirection.DownRight, new FastVector2Int(-1, 1) }, | |
| }; | |
| private static readonly Dictionary<FastVector2Int, GridDirection> InverseEvenYOffsetLookup = new(NeighborCount) | |
| { | |
| { new FastVector2Int(0, 1), GridDirection.UpRight }, | |
| { new FastVector2Int(1, 0), GridDirection.Up }, | |
| { new FastVector2Int(0, -1), GridDirection.UpLeft }, | |
| { new FastVector2Int(-1, -1), GridDirection.DownLeft }, | |
| { new FastVector2Int(-1, 0), GridDirection.Down }, | |
| { new FastVector2Int(-1, 1), GridDirection.DownRight }, | |
| }; | |
| public static readonly Dictionary<GridDirection, FastVector2Int> AdditionalEvenYOffsetLookup = new(4) | |
| { | |
| { GridDirection.UpLeft, new FastVector2Int(1, -1) }, | |
| { GridDirection.UpRight, new FastVector2Int(1, 1) }, | |
| { GridDirection.DownRight, new FastVector2Int(-2, 1) }, | |
| { GridDirection.DownLeft, new FastVector2Int(-2, -1) } | |
| }; | |
| private static readonly Dictionary<FastVector2Int, GridDirection> InverseAdditionalEvenYOffsetLookup = new(4) | |
| { | |
| { new FastVector2Int(1, -1), GridDirection.UpLeft }, | |
| { new FastVector2Int(1, 1), GridDirection.UpRight }, | |
| { new FastVector2Int(-2, 1), GridDirection.DownRight }, | |
| { new FastVector2Int(-2, -1), GridDirection.DownLeft }, | |
| }; | |
| public static readonly Dictionary<GridDirection, FastVector2Int> OddYOffsetLookup = new(NeighborCount) | |
| { | |
| { GridDirection.DownRight, new FastVector2Int(0, 1) }, | |
| { GridDirection.UpRight, new FastVector2Int(1, 1) }, | |
| { GridDirection.Up, new FastVector2Int(1, 0) }, | |
| { GridDirection.UpLeft, new FastVector2Int(1, -1) }, | |
| { GridDirection.DownLeft, new FastVector2Int(0, -1) }, | |
| { GridDirection.Down, new FastVector2Int(-1, 0) }, | |
| }; | |
| private static readonly Dictionary<FastVector2Int, GridDirection> InverseOddYOffsetLookup = new(NeighborCount) | |
| { | |
| { new FastVector2Int(0, 1), GridDirection.DownRight }, | |
| { new FastVector2Int(1, 1), GridDirection.UpRight }, | |
| { new FastVector2Int(1, 0), GridDirection.Up }, | |
| { new FastVector2Int(1, -1), GridDirection.UpLeft }, | |
| { new FastVector2Int(0, -1) , GridDirection.DownLeft }, | |
| { new FastVector2Int(-1, 0), GridDirection.Down }, | |
| }; | |
| public static readonly Dictionary<GridDirection, FastVector2Int> AdditionalYOddOffsetLookup = new(4) | |
| { | |
| { GridDirection.UpLeft, new FastVector2Int(2, -1) }, | |
| { GridDirection.UpRight, new FastVector2Int(2, 1)} , | |
| { GridDirection.DownRight, new FastVector2Int(-1, 1) }, | |
| { GridDirection.DownLeft, new FastVector2Int(-1, -1) } | |
| }; | |
| private static readonly Dictionary<FastVector2Int, GridDirection> InverseAdditionalYOddOffsetLookup = new(4) | |
| { | |
| { new FastVector2Int(2, -1), GridDirection.UpLeft }, | |
| { new FastVector2Int(2, 1), GridDirection.UpRight }, | |
| { new FastVector2Int(-1, 1), GridDirection.DownRight }, | |
| { new FastVector2Int(-1, -1), GridDirection.DownLeft } | |
| }; | |
| public static FastVector2Int EvenToOdd(FastVector2Int offset) | |
| { | |
| return TransformOffset(offset, InverseEvenYOffsetLookup, InverseAdditionalEvenYOffsetLookup, OddYOffsetLookup, AdditionalYOddOffsetLookup); | |
| } | |
| public static FastVector2Int OddToEven(FastVector2Int offset) | |
| { | |
| return TransformOffset(offset, InverseOddYOffsetLookup, InverseAdditionalYOddOffsetLookup, EvenYOffsetLookup, AdditionalEvenYOffsetLookup); | |
| } | |
| private static Vector2Int TransformOffset(FastVector2Int offset, Dictionary<FastVector2Int, GridDirection> fromLookup, Dictionary<FastVector2Int, GridDirection> additionalFromLookup, Dictionary<GridDirection, FastVector2Int> toLookup, Dictionary<GridDirection, FastVector2Int> additionalToLookup) | |
| { | |
| FastVector2Int modOffset = new(offset.x % 2, offset.y % 2); | |
| if (fromLookup.TryGetValue(modOffset, out GridDirection direction)) | |
| { | |
| FastVector2Int newOffset = toLookup[direction]; | |
| return offset + newOffset - modOffset; | |
| } | |
| if (additionalFromLookup.TryGetValue(modOffset, out direction)) | |
| { | |
| FastVector2Int newOffset = additionalToLookup[direction]; | |
| return offset + newOffset - modOffset; | |
| } | |
| return offset; | |
| } | |
| /* | |
| Use these offsets to find neighbors for hexes with an even y (position.y % 2 == 0) | |
| to find neighboring tiles. | |
| */ | |
| public static readonly GridOffset EvenYOffset = new GridOffset(new[] | |
| { | |
| new FastVector2Int(0, 1), // UpRight | |
| new FastVector2Int(1, 0), // Up | |
| new FastVector2Int(0, -1), // UpLeft | |
| new FastVector2Int(-1, -1), // DownLeft | |
| new FastVector2Int(-1, 0), // Down | |
| new FastVector2Int(-1, 1) // DownRight | |
| }, new Dictionary<FastVector2Int, List<FastVector2Int>>(NeighborCount) | |
| { | |
| [new FastVector2Int(0, -2)] = new List<FastVector2Int>(2) { new FastVector2Int(0, -1), new FastVector2Int(-1, -1) }, // Left | |
| [new FastVector2Int(1, -1)] = new List<FastVector2Int>(2) { new FastVector2Int(0, -1), new FastVector2Int(1, 0) }, // UpLeft | |
| [new FastVector2Int(1, 1)] = new List<FastVector2Int>(2) { new FastVector2Int(1, 0), new FastVector2Int(0, 1) }, // UpRight | |
| [new FastVector2Int(0, 2)] = new List<FastVector2Int>(2) { new FastVector2Int(0, 1), new FastVector2Int(-1, 1) }, // Right | |
| [new FastVector2Int(-2, 1)] = new List<FastVector2Int>(2) { new FastVector2Int(-1, 1), new FastVector2Int(-1, 0) }, // DownRight | |
| [new FastVector2Int(-2, -1)] = new List<FastVector2Int>(2) { new FastVector2Int(-1, 0), new FastVector2Int(-1, -1) }// DownLeft | |
| }); | |
| public static readonly GridOffset OddYOffset = new GridOffset(new[] | |
| { | |
| new FastVector2Int(0, 1), // DownRight | |
| new FastVector2Int(1, 1), // UpRight | |
| new FastVector2Int(1, 0), // Up | |
| new FastVector2Int(1, -1), // UpLeft | |
| new FastVector2Int(0, -1), // DownLeft | |
| new FastVector2Int(-1, 0), // Down | |
| }, new Dictionary<FastVector2Int, List<FastVector2Int>>(NeighborCount) | |
| { | |
| [new FastVector2Int(2, 1)] = new List<FastVector2Int>(2) { new FastVector2Int(1, 1), new FastVector2Int(1, 0) }, // UpRight | |
| [new FastVector2Int(0, 2)] = new List<FastVector2Int>(2) { new FastVector2Int(1, 1), new FastVector2Int(0, 1) }, // Right | |
| [new FastVector2Int(-1, 1)] = new List<FastVector2Int>(2) { new FastVector2Int(0, 1), new FastVector2Int(-1, 0) }, // DownRight | |
| [new FastVector2Int(-1, -1)] = new List<FastVector2Int>(2) { new FastVector2Int(0, -1), new FastVector2Int(-1, 0) }, // DownLeft | |
| [new FastVector2Int(0, -2)] = new List<FastVector2Int>(2) { new FastVector2Int(0, -1), new FastVector2Int(1, -1) }, // Left | |
| [new FastVector2Int(2, -1)] = new List<FastVector2Int>(2) { new FastVector2Int(1, -1), new FastVector2Int(1, 0) }, // UpLeft | |
| }); | |
| } | |
| public static class GridOffsetExtensions | |
| { | |
| public static GridOffset.GridDirection AsGridDirection(this Vector2 vector) | |
| { | |
| if (vector is { x: 0, y: 0 }) | |
| { | |
| return GridOffset.GridDirection.Up; | |
| } | |
| float angle; | |
| if (vector.x < 0) | |
| { | |
| angle = 360 - Mathf.Atan2(vector.x, vector.y) * Mathf.Rad2Deg * -1; | |
| } | |
| else | |
| { | |
| angle = Mathf.Atan2(vector.x, vector.y) * Mathf.Rad2Deg; | |
| } | |
| if (315 <= angle || angle < 45) | |
| { | |
| return GridOffset.GridDirection.Up; | |
| } | |
| if (45 <= angle && angle < 90) | |
| { | |
| return GridOffset.GridDirection.UpRight; | |
| } | |
| if (90 <= angle && angle < 135) | |
| { | |
| return GridOffset.GridDirection.DownRight; | |
| } | |
| if (135 <= angle && angle < 225) | |
| { | |
| return GridOffset.GridDirection.Down; | |
| } | |
| if (225 <= angle && angle < 270) | |
| { | |
| return GridOffset.GridDirection.DownLeft; | |
| } | |
| if (270 <= angle && angle < 315) | |
| { | |
| return GridOffset.GridDirection.UpLeft; | |
| } | |
| throw new Exception($"Invalid angle {angle:0.00}"); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment