Adding Grid Coordinates System, and a testScript

This commit is contained in:
Matt F 2026-04-26 12:36:42 -07:00
parent 3cd4bada37
commit 0ed4df8bc9
7 changed files with 792 additions and 0 deletions

View file

@ -0,0 +1,140 @@
using System.Collections.Generic;
using UnityEngine;
namespace TD.Core
{
/// <summary>
/// Pure math utility for converting between tile-grid coordinates and world-space
/// coordinates, plus common grid helpers (neighbors, distance, adjacency).
///
/// This class knows nothing about maps, walkability, zones, or game state. It only
/// answers: "where in the world is tile (x, y)?" and "what tile contains world point P?"
///
/// Conventions:
/// - Tiles are 1.0 world unit on each side (TILE_SIZE).
/// - Tiles are CENTER-BASED: tile (0, 0) has its center at world (0, 0, 0)
/// and occupies world XZ from (-0.5, -0.5) to (+0.5, +0.5).
/// - The grid lives on the XZ plane at Y = BUILDABLE_PLANE_Y. Grid-y maps to world-z.
/// - 4-connected (no diagonals).
/// </summary>
public static class GridCoordinates
{
// ----- Constants -----------------------------------------------------------
/// <summary>World units per tile edge. Single source of truth for tile sizing.</summary>
public const float TILE_SIZE = 1.0f;
/// <summary>Y coordinate of the buildable plane in world space.</summary>
public const float BUILDABLE_PLANE_Y = 0.0f;
// ----- Core conversions ----------------------------------------------------
/// <summary>
/// Returns the world-space center of the given tile, on the buildable plane.
/// Use this for placing towers, drawing ghost previews, and for A* path waypoints.
/// </summary>
public static Vector3 GridToWorld(Vector2Int gridPos)
{
return new Vector3(
gridPos.x * TILE_SIZE,
BUILDABLE_PLANE_Y,
gridPos.y * TILE_SIZE);
}
/// <summary>
/// Returns the tile that contains the given world position.
/// The Y component of worldPos is ignored. Uses round-to-nearest because
/// tiles are center-based — any world point within ±0.5 of a tile's center
/// belongs to that tile.
/// </summary>
public static Vector2Int WorldToGrid(Vector3 worldPos)
{
return new Vector2Int(
Mathf.RoundToInt(worldPos.x / TILE_SIZE),
Mathf.RoundToInt(worldPos.z / TILE_SIZE));
}
/// <summary>
/// Overload for callers that already have a 2D XZ position (e.g., UI raycast hits
/// projected onto the buildable plane).
/// </summary>
public static Vector2Int WorldToGrid(Vector2 worldPosXZ)
{
return new Vector2Int(
Mathf.RoundToInt(worldPosXZ.x / TILE_SIZE),
Mathf.RoundToInt(worldPosXZ.y / TILE_SIZE));
}
// ----- Grid helpers --------------------------------------------------------
/// <summary>
/// True if two tiles share an edge (4-connected). Diagonal neighbors return false.
/// </summary>
public static bool IsAdjacent(Vector2Int a, Vector2Int b)
{
int dx = Mathf.Abs(a.x - b.x);
int dy = Mathf.Abs(a.y - b.y);
return (dx + dy) == 1;
}
/// <summary>
/// Manhattan distance between two tiles. This is the natural admissible
/// heuristic for A* on a 4-connected grid with uniform step cost.
/// </summary>
public static int ManhattanDistance(Vector2Int a, Vector2Int b)
{
return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
}
/// <summary>
/// Yields the four cardinal neighbors of the given tile (N, E, S, W).
/// Does NOT check walkability or map bounds — that is the caller's job.
/// </summary>
public static IEnumerable<Vector2Int> GetNeighbors(Vector2Int tile)
{
yield return new Vector2Int(tile.x, tile.y + 1); // North (+z)
yield return new Vector2Int(tile.x + 1, tile.y); // East (+x)
yield return new Vector2Int(tile.x, tile.y - 1); // South (-z)
yield return new Vector2Int(tile.x - 1, tile.y); // West (-x)
}
// ----- Footprint helpers ---------------------------------------------------
//
// Towers occupy a footprint of N x M tiles anchored at a single tile coordinate.
// The anchor is the SOUTHWEST (minimum-x, minimum-y) corner of the footprint.
// For a 2x2 tower at anchor (5, 7), the footprint covers:
// (5, 7), (6, 7), (5, 8), (6, 8)
/// <summary>
/// Yields every tile covered by a footprint of the given size, anchored
/// at the given tile (anchor is the southwest corner of the footprint).
/// </summary>
public static IEnumerable<Vector2Int> GetFootprintTiles(Vector2Int anchor, Vector2Int footprintSize)
{
for (int dx = 0; dx < footprintSize.x; dx++)
{
for (int dy = 0; dy < footprintSize.y; dy++)
{
yield return new Vector2Int(anchor.x + dx, anchor.y + dy);
}
}
}
/// <summary>
/// Returns the world-space center of a footprint anchored at the given tile.
/// For a 2x2 footprint at anchor (5, 7) with TILE_SIZE = 1.0, returns (5.5, 0, 7.5).
/// Use this to position the tower's visual GameObject so it sits centered on its
/// footprint rather than on the anchor tile's center.
/// </summary>
public static Vector3 GetFootprintCenterWorld(Vector2Int anchor, Vector2Int footprintSize)
{
float centerOffsetX = (footprintSize.x - 1) * 0.5f * TILE_SIZE;
float centerOffsetZ = (footprintSize.y - 1) * 0.5f * TILE_SIZE;
Vector3 anchorWorld = GridToWorld(anchor);
return new Vector3(
anchorWorld.x + centerOffsetX,
anchorWorld.y,
anchorWorld.z + centerOffsetZ);
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 55f1a840195c0684786a2ddcf30d5865

View file

@ -0,0 +1,12 @@
using UnityEngine;
using TD.Core;
public class GridCoordsTest : MonoBehaviour
{
void Start()
{
Debug.Log($"Tile (5,7) world center: {GridCoordinates.GridToWorld(new Vector2Int(5, 7))}");
Debug.Log($"World (5.3, 0, 6.8) maps to tile: {GridCoordinates.WorldToGrid(new Vector3(5.3f, 0, 6.8f))}");
Debug.Log($"2x2 footprint at (5,7) center: {GridCoordinates.GetFootprintCenterWorld(new Vector2Int(5, 7), new Vector2Int(2, 2))}");
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9c2af3b2731bd314e8502ac9db3a8bc5