using UnityEngine; namespace AibisDream { /// /// 放置验证器实现 /// 负责验证形状是否可以放置在指定位置 /// public static class PlacementValidator { public static SnapResult CheckSnapToGrid(this IBlockPuzzleGrid grid, BlockShape shape) { // 预制错误结果作为返回值 SnapResult result = new SnapResult { canSnap = false, snapPosition = shape.transform.position, gridX = 0, gridY = 0, distance = float.MaxValue, gridPositions = new Vector2Int[0] }; // 将Shape的世界坐标转换为网格坐标(可以是虚拟坐标) bool inGrid = grid.WorldToGrid(shape.transform.position, out int gridX, out int gridY); if (!inGrid) return result; // 将形状的所有格子转换为网格坐标 // gridX, gridY 是形状的 (0, 0) 点在网格中的位置 Vector2Int[] gridCells = ConvertShapeToGridCells(shape, gridX, gridY); result.gridX = gridX; result.gridY = gridY; result.snapPosition = grid.GridToWorld(gridX, gridY); result.gridPositions = gridCells; // 如果不在网格中,则返回结果 if (!inGrid) return result; // 验证形状的限制条件 if (!shape.CheckExtraLimits(new Vector2Int(gridX, gridY))) return result; // 逐个坐标验证是否可以放置 var canPlace = grid.CanPlace(gridCells); if (!canPlace) return result; result.canSnap = true; result.distance = Vector3.Distance(shape.transform.position, result.snapPosition); return result; } /// /// 将形状的相对坐标转换为网格坐标 /// 已知形状的 (0, 0) 点在网格中的位置为 (gridX, gridY),将形状的其他格子坐标转换为网格坐标 /// 注意:GetBlockPositions() 返回的坐标是基于旋转后的pattern的索引坐标 /// 旋转后pattern的坐标轴方向可能改变,需要使用GetAxis()来处理坐标轴的翻转 /// /// 形状 /// 网格X坐标(形状左下角位置,即形状的 (0, 0) 点对应的网格坐标) /// 网格Y坐标(形状左下角位置,即形状的 (0, 0) 点对应的网格坐标) /// 形状所有格子在网格中的坐标数组 public static Vector2Int[] ConvertShapeToGridCells(IBlockShape shape, int gridX, int gridY) { if (shape == null) { return new Vector2Int[0]; } Vector2Int[] blockPositions = shape.GetBlockPositions(); if (blockPositions == null || blockPositions.Length == 0) { return new Vector2Int[0]; } Vector2Int[] gridCells = new Vector2Int[blockPositions.Length]; for (int i = 0; i < blockPositions.Length; i++) { // 将形状的相对坐标转换为网格坐标 // 形状的 (0, 0) 对应网格的 (gridX, gridY) // 旋转后pattern的坐标轴方向可能改变,需要使用GetAxis()来处理坐标轴的翻转 // 例如:Rotate90时,X轴方向翻转(axis.x = -1),Y轴方向不变(axis.y = 1) gridCells[i] = new Vector2Int( gridX + blockPositions[i].x, gridY + blockPositions[i].y ); } return gridCells; } /// /// 为吸附制作预览 /// /// 转换后的网格坐标组 public static void ShowSnapPreview(this IBlockPuzzleGrid grid, SnapResult snapResult) { grid.ResetPreviewGrid(); // 如果可以吸附 if (snapResult.canSnap) { foreach (var cell in snapResult.gridPositions) { grid.SetPreviewCellState(cell.x, cell.y, GridCellState.PreviewVaild); } } // 如果不能吸附 else { foreach (var cell in snapResult.gridPositions) { if (grid.IsValidPosition(cell.x, cell.y)) { grid.SetPreviewCellState(cell.x, cell.y, GridCellState.PreviewInvalid); } } } grid.GridVisualizer.UpdateAllCellsVisual(); } public static void TrySnapToGrid(this IBlockPuzzleGrid grid, BlockShape shape, Vector2Int rootCell) { // 判断RootCell的位置 var rootWorldPos = grid.GridToWorld(rootCell.x, rootCell.y); // 已经判断是否可吸附,直接吸就好了 shape.transform.position = rootWorldPos; // 设置网格状态 var shapeCells = ConvertShapeToGridCells(shape, rootCell.x, rootCell.y); foreach (var cell in shapeCells) { grid.SetCellState(cell.x, cell.y, GridCellState.Occupied); } // 加入网格 var gridShapeData = new GridShapeData { shapeKey = shape.ShapeName, rootCell = rootCell, rotationAngle = shape.CurrentRotation }; grid.ShapeDict.Add(shape, gridShapeData); } /// /// 尝试从网格中移除形状 /// /// 网格 /// 形状 public static void TryRemoveShapeFromGrid(this IBlockPuzzleGrid grid, BlockShape shape) { if (grid.ShapeDict.TryGetValue(shape, out var gridShapeData)) { var rootCell = gridShapeData.rootCell; // 设置网格状态 var shapeCells = ConvertShapeToGridCells(shape, rootCell.x, rootCell.y); foreach (var cell in shapeCells) { grid.SetCellState(cell.x, cell.y, GridCellState.Empty); } grid.ShapeDict.Remove(shape); } } } /// /// 放置验证结果 /// public enum PlacementResult { Success, // 可以放置 OutOfBounds, // 超出边界 Overlap, // 与已有块重叠 InvalidPosition // 无效位置 } /// /// 吸附检测结果 /// public struct SnapResult { public bool canSnap; // 是否可以吸附 public Vector3 snapPosition; // 吸附后的世界坐标位置 public int gridX; // 吸附到的网格X坐标(形状左下角位置) public int gridY; // 吸附到的网格Y坐标(形状左下角位置) public float distance; // 到网格单元格的距离 public Vector2Int[] gridPositions; // 吸附后 public Vector2Int RootCell => new Vector2Int(gridX, gridY); // 吸附后的根网格坐标 public static SnapResult RecycleResult => new() { canSnap = false, snapPosition = Vector3.zero, gridX = -10, gridY = -10, distance = float.MaxValue, gridPositions = new Vector2Int[0] }; }; }