using UnityEngine; namespace Mirror { internal class HexGrid2D { // Radius of each hexagonal cell (half the width) internal float cellRadius; // Offset applied to align the grid with the world origin Vector2 originOffset; // Precomputed constants for hexagon math to improve performance readonly float sqrt3Div3; // sqrt(3) / 3, used in coordinate conversions readonly float oneDiv3; // 1 / 3, used in coordinate conversions readonly float twoDiv3; // 2 / 3, used in coordinate conversions readonly float sqrt3; // sqrt(3), used in world coordinate calculations readonly float sqrt3Div2; // sqrt(3) / 2, used in world coordinate calculations internal HexGrid2D(ushort visRange) { // Set cell radius as half the visibility range cellRadius = visRange / 2f; // Offset to center the grid at world origin (2D XZ plane) originOffset = Vector2.zero; // Precompute mathematical constants for efficiency sqrt3Div3 = Mathf.Sqrt(3) / 3f; oneDiv3 = 1f / 3f; twoDiv3 = 2f / 3f; sqrt3 = Mathf.Sqrt(3); sqrt3Div2 = Mathf.Sqrt(3) / 2f; } // Precomputed array of neighbor offsets as Cell2D structs (center + 6 neighbors) static readonly Cell2D[] neighborCellsBase = new Cell2D[] { new Cell2D(0, 0), // Center new Cell2D(1, -1), // Top-right new Cell2D(1, 0), // Right new Cell2D(0, 1), // Bottom-right new Cell2D(-1, 1), // Bottom-left new Cell2D(-1, 0), // Left new Cell2D(0, -1) // Top-left }; // Converts a grid cell (q, r) to a world position (x, z) internal Vector2 CellToWorld(Cell2D cell) { // Calculate X and Z using hexagonal coordinate formulas float x = cellRadius * (sqrt3 * cell.q + sqrt3Div2 * cell.r); float z = cellRadius * (1.5f * cell.r); // Subtract the origin offset to align with world space and return the position return new Vector2(x, z) - originOffset; } // Converts a world position (x, z) to a grid cell (q, r) internal Cell2D WorldToCell(Vector2 position) { // Apply the origin offset to adjust the position before conversion position += originOffset; // Convert world X, Z to axial q, r coordinates using inverse hexagonal formulas float q = (sqrt3Div3 * position.x - oneDiv3 * position.y) / cellRadius; float r = (twoDiv3 * position.y) / cellRadius; // Round to the nearest valid cell and return return RoundToCell(q, r); } // Rounds floating-point axial coordinates (q, r) to the nearest integer cell coordinates Cell2D RoundToCell(float q, float r) { // Calculate the third hexagonal coordinate (s) for consistency float s = -q - r; int qInt = Mathf.RoundToInt(q); // Round q to nearest integer int rInt = Mathf.RoundToInt(r); // Round r to nearest integer int sInt = Mathf.RoundToInt(s); // Round s to nearest integer // Calculate differences to determine which coordinate needs adjustment float qDiff = Mathf.Abs(q - qInt); float rDiff = Mathf.Abs(r - rInt); float sDiff = Mathf.Abs(s - sInt); // Adjust q or r based on which has the largest rounding error (ensures q + r + s = 0) if (qDiff > rDiff && qDiff > sDiff) qInt = -rInt - sInt; // Adjust q if it has the largest error else if (rDiff > sDiff) rInt = -qInt - sInt; // Adjust r if it has the largest error return new Cell2D(qInt, rInt); } // Populates the provided array with neighboring cells around a given center cell internal void GetNeighborCells(Cell2D center, Cell2D[] neighbors) { // Ensure the array has the correct size (7: center + 6 neighbors) if (neighbors.Length != 7) throw new System.ArgumentException("Neighbor array must have exactly 7 elements"); // Populate the array by adjusting precomputed offsets with the center cell's coordinates for (int i = 0; i < neighborCellsBase.Length; i++) { neighbors[i] = new Cell2D( center.q + neighborCellsBase[i].q, center.r + neighborCellsBase[i].r ); } } #if UNITY_EDITOR // Draws a 2D hexagonal gizmo in the Unity Editor for visualization internal void DrawHexGizmo(Vector3 center, float radius, HexSpatialHash2DInterestManagement.CheckMethod checkMethod) { // Hexagon has 6 sides const int segments = 6; // Array to store the 6 corner points in 3D Vector3[] corners = new Vector3[segments]; // Calculate the corner positions based on the plane (XZ or XY) for (int i = 0; i < segments; i++) { // Angle for each corner, offset by 90 degrees float angle = 2 * Mathf.PI / segments * i + Mathf.PI / 2; if (checkMethod == HexSpatialHash2DInterestManagement.CheckMethod.XZ_FOR_3D) { // XZ plane: flat hexagon, Y=0 corners[i] = center + new Vector3(radius * Mathf.Cos(angle), 0, radius * Mathf.Sin(angle)); } else // XY_FOR_2D { // XY plane: vertical hexagon, Z=0 corners[i] = center + new Vector3(radius * Mathf.Cos(angle), radius * Mathf.Sin(angle), 0); } } // Draw each side of the hexagon for (int i = 0; i < segments; i++) { Vector3 cornerA = corners[i]; Vector3 cornerB = corners[(i + 1) % segments]; Gizmos.DrawLine(cornerA, cornerB); } } #endif } // Struct representing a single cell in the 2D hexagonal grid internal struct Cell2D { internal readonly int q; // Axial q coordinate (horizontal axis) internal readonly int r; // Axial r coordinate (diagonal axis) internal Cell2D(int q, int r) { this.q = q; this.r = r; } public override bool Equals(object obj) => obj is Cell2D other && q == other.q && r == other.r; // Generate a unique hash code for the cell public override int GetHashCode() => (q << 16) ^ r; } }