using System.Collections.Generic; using AibisDream.Utility; using UnityEngine; namespace AibisDream { public class BlockShape : MonoBehaviour, IBlockShape { private SpriteRenderer[,] _cellRenderers; // 模块数据 [Header("模块数据")] [SerializeField] private int shapeId; [SerializeField] private string shapeName; [SerializeField] private RotationAngle currentRotation; [SerializeField] private int width; [SerializeField] private int height; [SerializeField] private float cellSize; [SerializeField] private string originalPatternStr; // 接口实现 public int ShapeId => shapeId; public string ShapeName => shapeName; public RotationAngle CurrentRotation => currentRotation; public int Width => width; public int Height => height; public int BlockCount { get { var pattern = GetOriginalPattern(); if (pattern == null) return 0; int count = 0; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (pattern[i, j] != 0) { count++; } } } return count; } } public Vector2Int[] GetBlockPositions() { var pattern = GetOriginalPattern(); if (pattern == null) return new Vector2Int[0]; List positions = new List(); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (pattern[i, j] != 0) { positions.Add(new Vector2Int(i, j)); } } } return positions.ToArray(); } /// /// 获取单元格渲染器数组(供拖拽组件使用) /// public SpriteRenderer[,] GetCellRenderers() { return _cellRenderers; } public int[,] GetCurrentPattern() { throw new System.NotImplementedException(); } public int[,] GetOriginalPattern() { return CommonUtil.DeserializeIntArray(originalPatternStr); } public bool HasBlockAt(int x, int y) { throw new System.NotImplementedException(); } public void ResetRotation() { throw new System.NotImplementedException(); } public void Rotate(RotationAngle angle) { throw new System.NotImplementedException(); } public void RotateClockwise() { throw new System.NotImplementedException(); } public void RotateCounterClockwise() { throw new System.NotImplementedException(); } private Sprite CreateDefaultSprite() { Texture2D texture = new Texture2D(1, 1); texture.SetPixel(0, 0, Color.red); texture.Apply(); return Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f), 1f); } // 仅限编辑器下使用 public void DrawShape() { transform.DestroyAllChildren(); _cellRenderers = null; // 根据Width和Height,绘制Cells,Cell为SpriteRenderer _cellRenderers = new SpriteRenderer[width, height]; var defaultSprite = CreateDefaultSprite(); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { var cell = new GameObject($"Cell_{i}_{j}"); var cellRenderer = cell.AddComponent(); cellRenderer.sprite = defaultSprite; _cellRenderers[i, j] = cellRenderer; cell.transform.parent = transform; // 计算Cell应处的位置 var cellPosX = i * cellSize; var cellPosY = j * cellSize; cell.transform.localPosition = new Vector3(cellPosX, cellPosY, 0); } } // 反序列化并处理 var pattern = CommonUtil.DeserializeIntArray(originalPatternStr); // 先判断pattern的维度是否与width和height一致 if (pattern.GetLength(0) != width || pattern.GetLength(1) != height) { // 如果不一致,就不处理 Debug.LogWarning($"Pattern dimension mismatch: {pattern.GetLength(0)}x{pattern.GetLength(1)} != {width}x{height}"); return; } // 如果一致,就读取pattern,并设置对应的cell的SpriteRenderer是否激活 for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { _cellRenderers[i, j].enabled = pattern[i, j] != 0; } } // 更新碰撞体形状 UpdatePolygonCollider(); } public void SaveShape() { // 如果_cellRenderers不为空,则序列化并保存 if (_cellRenderers != null) { var pattern = new int[width, height]; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { pattern[i, j] = _cellRenderers[i, j].enabled ? 1 : 0; } } Debug.Log($"SaveShape: {CommonUtil.SerializeIntArray(pattern)}"); originalPatternStr = CommonUtil.SerializeIntArray(pattern); // 保存后更新碰撞体 UpdatePolygonCollider(); } } /// /// 根据originalPattern更新PolygonCollider2D的形状 /// public void UpdatePolygonCollider() { var pattern = GetOriginalPattern(); if (pattern == null) return; // 获取或添加PolygonCollider2D组件 PolygonCollider2D polygonCollider = GetComponent(); if (polygonCollider == null) { polygonCollider = gameObject.AddComponent(); } // 生成多边形顶点 List vertices = GeneratePolygonVertices(pattern); if (vertices.Count > 0) { // 设置多边形路径 polygonCollider.pathCount = 1; polygonCollider.SetPath(0, vertices.ToArray()); } else { // 如果没有有效格子,清除路径 polygonCollider.pathCount = 0; } } /// /// 根据pattern生成多边形顶点 /// private List GeneratePolygonVertices(int[,] pattern) { List vertices = new List(); if (pattern == null || width <= 0 || height <= 0 || cellSize <= 0) return vertices; // 生成精确的外轮廓(只包含边界点) vertices = GenerateOutlineVertices(pattern); return vertices; } /// /// 生成精确的外轮廓顶点(只包含边界点) /// 使用Marching Squares算法的简化版本 /// private List GenerateOutlineVertices(int[,] pattern) { List outlineVertices = new List(); HashSet visitedPoints = new HashSet(); // 遍历所有格子,找到边界点 for (int i = 0; i <= width; i++) { for (int j = 0; j <= height; j++) { // 检查这个角点是否在边界上 bool isBoundaryCorner = IsBoundaryCorner(pattern, i, j); if (isBoundaryCorner) { // 计算角点的本地坐标 // cornerX 和 cornerY 是角点索引(0到width/height) // 需要转换为格子的角点坐标 // 角点 (i, j) 对应格子 (i-0.5, j-0.5) 的角点位置 float x = (i - 0.5f) * cellSize; float y = (j - 0.5f) * cellSize; Vector2Int key = new Vector2Int(Mathf.RoundToInt(x * 100), Mathf.RoundToInt(y * 100)); if (!visitedPoints.Contains(key)) { outlineVertices.Add(new Vector2(x, y)); visitedPoints.Add(key); } } } } // 如果没有找到边界点,使用简单包围盒 if (outlineVertices.Count == 0) { return GenerateBoundingBox(pattern); } // 对顶点进行排序,形成闭合多边形 return OrderVerticesForPolygon(outlineVertices); } /// /// 检查角点是否在边界上 /// private bool IsBoundaryCorner(int[,] pattern, int cornerX, int cornerY) { // 角点位于四个格子的交汇处 // 检查周围四个格子的状态 bool topLeft = (cornerX > 0 && cornerY > 0) ? pattern[cornerX - 1, cornerY - 1] != 0 : false; bool topRight = (cornerX < width && cornerY > 0) ? pattern[cornerX, cornerY - 1] != 0 : false; bool bottomLeft = (cornerX > 0 && cornerY < height) ? pattern[cornerX - 1, cornerY] != 0 : false; bool bottomRight = (cornerX < width && cornerY < height) ? pattern[cornerX, cornerY] != 0 : false; // 如果四个格子状态不一致,说明这个角点在边界上 int filledCount = (topLeft ? 1 : 0) + (topRight ? 1 : 0) + (bottomLeft ? 1 : 0) + (bottomRight ? 1 : 0); // 边界角点:不是全部填充也不是全部空 return filledCount > 0 && filledCount < 4; } /// /// 生成简单的包围盒(备用方法) /// private List GenerateBoundingBox(int[,] pattern) { List vertices = new List(); // 找到有效格子的边界 int minX = width, maxX = -1, minY = height, maxY = -1; bool hasValidCell = false; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (pattern[i, j] != 0) { hasValidCell = true; minX = Mathf.Min(minX, i); maxX = Mathf.Max(maxX, i); minY = Mathf.Min(minY, j); maxY = Mathf.Max(maxY, j); } } } if (!hasValidCell) return vertices; // 计算包围盒的四个角点(本地坐标) float halfSize = cellSize / 2f; float minXWorld = minX * cellSize - width * cellSize / 2f - halfSize; float maxXWorld = (maxX + 1) * cellSize - width * cellSize / 2f + halfSize; float minYWorld = minY * cellSize - height * cellSize / 2f - halfSize; float maxYWorld = (maxY + 1) * cellSize - height * cellSize / 2f + halfSize; // 按逆时针顺序添加顶点(Unity的PolygonCollider2D要求) vertices.Add(new Vector2(minXWorld, minYWorld)); // 左下 vertices.Add(new Vector2(maxXWorld, minYWorld)); // 右下 vertices.Add(new Vector2(maxXWorld, maxYWorld)); // 右上 vertices.Add(new Vector2(minXWorld, maxYWorld)); // 左上 return vertices; } /// /// 对顶点进行排序,形成闭合多边形 /// private List OrderVerticesForPolygon(List vertices) { if (vertices.Count <= 2) return vertices; // 找到中心点 Vector2 center = Vector2.zero; foreach (var v in vertices) { center += v; } center /= vertices.Count; // 按角度排序(相对于中心点) vertices.Sort((a, b) => { float angleA = Mathf.Atan2(a.y - center.y, a.x - center.x); float angleB = Mathf.Atan2(b.y - center.y, b.x - center.x); if (angleA.CompareTo(angleB) != 0) { return angleA.CompareTo(angleB); } return (a - center).sqrMagnitude.CompareTo((b - center).sqrMagnitude); }); return vertices; } } }