using UnityEngine; /// /// 部署网格的几何参数与坐标换算。 /// 网格与场景里用于标记布阵区域的那个 Plane 完全重合: /// Plane 世界范围 X -5.75..5.75、Z -15.6..-4.4,中心 (0, ?, -10)。 /// 5 列 × 4 行,奇数列数可保证中路(第3列/索引2)完美居中对战; /// 整体位于 NavMesh 覆盖范围内。 /// public static class DeployGrid { public const int Cols = 5; public const int Rows = 4; public const float ColStep = 2.30f; // 11.5 / 5 = 2.3 public const float RowStep = 2.80f; // 11.2 / 4 = 2.8 public const float BackZ = -14.2f; // 第 0 排(最靠近镜头)中心 Z = -10 - 1.5*2.8 public const float CellWidth = 2.22f; // 2.3 * 0.965,留出网格缝隙 public const float CellDepth = 2.74f; public const float CellHeight = 0.03f; public const float GridY = 0.115f; // 格位可视块中心高度(贴在布阵平面之上) public const float UnitY = 0.10f; // 单位落点高度(与烘焙后的 NavMesh 高度一致) public const int CellCount = Cols * Rows; public static int ToIndex(int row, int col) => row * Cols + col; public static int RowOf(int index) => index / Cols; public static int ColOf(int index) => index % Cols; public static bool IsValidIndex(int index) => index >= 0 && index < CellCount; /// 格位中心的世界坐标(单位落点高度)。 public static Vector3 CellPosition(int index) { int r = RowOf(index); int c = ColOf(index); float x = (c - (Cols - 1) * 0.5f) * ColStep; float z = BackZ + r * RowStep; return new Vector3(x, UnitY, z); } /// 整个网格的中心(可视块高度)。 public static Vector3 GridCenter { get { float z = BackZ + (Rows - 1) * RowStep * 0.5f; return new Vector3(0f, GridY, z); } } /// 网格在 X 方向的宽度。 public static float GridWidth => Cols * ColStep; /// 网格在 Z 方向的深度。 public static float GridDepth => Rows * RowStep; /// 把地面坐标吸附到最近格位;不在网格范围内返回 -1。 public static int SnapToCell(Vector3 world) { int c = Mathf.RoundToInt(world.x / ColStep + (Cols - 1) * 0.5f); int r = Mathf.RoundToInt((world.z - BackZ) / RowStep); if (c < 0 || c >= Cols || r < 0 || r >= Rows) { return -1; } Vector3 center = CellPosition(ToIndex(r, c)); if (Mathf.Abs(center.x - world.x) > ColStep * 0.5f) return -1; if (Mathf.Abs(center.z - world.z) > RowStep * 0.5f) return -1; return ToIndex(r, c); } /// /// 宽松吸附:超出网格时夹到最近格位,只要离网格边缘不超过 tolerance 个格子就算命中。 /// 让玩家在网格边缘附近松手也能放下单位。 /// public static int SnapToCellClamped(Vector3 world, float tolerance = 0.9f) { int c = Mathf.RoundToInt(world.x / ColStep + (Cols - 1) * 0.5f); int r = Mathf.RoundToInt((world.z - BackZ) / RowStep); int clampedC = Mathf.Clamp(c, 0, Cols - 1); int clampedR = Mathf.Clamp(r, 0, Rows - 1); // 超出网格多少(以格为单位) int overC = c < 0 ? -c : (c >= Cols ? c - (Cols - 1) : 0); int overR = r < 0 ? -r : (r >= Rows ? r - (Rows - 1) : 0); if (overC > tolerance || overR > tolerance) { return -1; } return ToIndex(clampedR, clampedC); } /// 把一组单位在网格宽度内均匀铺开,返回列号。 public static int SpreadColumn(int indexInGroup, int groupCount) { if (groupCount <= 1) return Cols / 2; if (groupCount >= Cols) return indexInGroup % Cols; float step = (Cols - 1f) / (groupCount + 1f); return Mathf.RoundToInt(step * (indexInGroup + 1)); } /// 默认阵型格位:近战在最靠敌方的一排,远程在最后排。 public static int DefaultCell(bool isMelee, int indexInGroup, int groupCount) { int row = isMelee ? Rows - 1 : 0; return ToIndex(row, SpreadColumn(indexInGroup, groupCount)); } }