using System; using System.Collections.Generic; using AibisDream.Utility; using UnityEngine; using AibisDream.Framework; namespace AibisDream { public class BlockShape : MonoBehaviour, IBlockShape { private SpriteRenderer[,] _cellRenderers; // 模块数据 [Header("模块数据")] [SerializeField] private BlockShapeData _blockShapeData; private RotationAngle currentRotation; // 接口实现 public ShapeDragger ShapeDragger { get; private set; } public int ShapeId => _blockShapeData.shapeId; public string ShapeName => _blockShapeData.shapeName; public RotationAngle CurrentRotation => currentRotation; public int Width => GetCurrentWidth(); public int Height => GetCurrentHeight(); public int BlockCount { get { var pattern = GetOriginalPattern(); if (pattern == null) return 0; int count = 0; for (int i = 0; i < _blockShapeData.width; i++) { for (int j = 0; j < _blockShapeData.height; j++) { if (pattern[i, j] != 0) { count++; } } } return count; } } public void Init(BlockShapeData blockShapeData) { ShapeDragger = GetComponent(); _blockShapeData = blockShapeData; // 加载Sprite LoadSprite(); // 更新碰撞体 UpdatePolygonCollider(); } public Vector2Int[] GetBlockPositions() { var pattern = GetCurrentPattern(); if (pattern == null) return new Vector2Int[0]; int currentWidth = GetCurrentWidth(); int currentHeight = GetCurrentHeight(); List positions = new List(); for (int i = 0; i < currentWidth; i++) { for (int j = 0; j < currentHeight; j++) { if (pattern[i, j] != 0) { positions.Add(new Vector2Int(i, j)); } } } return positions.ToArray(); } public int[,] GetCurrentPattern() { var originalPattern = GetOriginalPattern(); if (originalPattern == null) return null; if (currentRotation == RotationAngle.Rotate0) { return originalPattern; } return RotatePattern(originalPattern, currentRotation); } public int[,] GetOriginalPattern() { return CommonUtil.DeserializeIntArray(_blockShapeData.originalPatternStr); } public bool HasBlockAt(int x, int y) { var pattern = GetCurrentPattern(); if (pattern == null) return false; int currentWidth = GetCurrentWidth(); int currentHeight = GetCurrentHeight(); if (x < 0 || x >= currentWidth || y < 0 || y >= currentHeight) return false; return pattern[x, y] != 0; } /// /// 根据网格情况计算出形状的物理中心位置 /// /// 形状的物理中心位置,z为0 public Vector3 GetCenterPosition() { var pattern = GetCurrentPattern(); if (pattern == null) return Vector3.zero; int currentWidth = GetCurrentWidth(); int currentHeight = GetCurrentHeight(); Vector3 centerSum = Vector3.zero; int blockCount = 0; // 遍历所有格子,计算填充格子的中心位置 for (int i = 0; i < currentWidth; i++) { for (int j = 0; j < currentHeight; j++) { if (pattern[i, j] != 0) { // 计算格子(i,j)的物理中心位置 // 格子左下角在 (i * cellSize, j * cellSize) // 格子中心在 (i * cellSize + cellSize/2, j * cellSize + cellSize/2) float cellCenterX = i * _blockShapeData.cellSize * GetAxis().x; float cellCenterY = j * _blockShapeData.cellSize * GetAxis().y; centerSum += new Vector3(cellCenterX, cellCenterY, 0); blockCount++; } } } // 如果没有填充的格子,返回零向量 if (blockCount == 0) return Vector3.zero; // 返回所有填充格子的平均位置(物理中心) return centerSum / blockCount; } public void ResetRotation() { currentRotation = RotationAngle.Rotate0; UpdateVisualRotation(); } public void Rotate(RotationAngle angle) { currentRotation = angle; UpdateVisualRotation(); } public void RotateClockwise() { // 顺时针旋转90度 switch (currentRotation) { case RotationAngle.Rotate0: currentRotation = RotationAngle.Rotate90; break; case RotationAngle.Rotate90: currentRotation = RotationAngle.Rotate180; break; case RotationAngle.Rotate180: currentRotation = RotationAngle.Rotate270; break; case RotationAngle.Rotate270: currentRotation = RotationAngle.Rotate0; break; } UpdateVisualRotation(); } public Vector2Int GetAxis() { // 顺时针旋转90度 return currentRotation switch { RotationAngle.Rotate0 => new Vector2Int(1, 1), RotationAngle.Rotate90 => new Vector2Int(-1, 1), RotationAngle.Rotate180 => new Vector2Int(-1, -1), RotationAngle.Rotate270 => new Vector2Int(1, -1), _ => new Vector2Int(1, 1), }; } public void RotateCounterClockwise() { // 逆时针旋转90度 switch (currentRotation) { case RotationAngle.Rotate0: currentRotation = RotationAngle.Rotate270; break; case RotationAngle.Rotate90: currentRotation = RotationAngle.Rotate0; break; case RotationAngle.Rotate180: currentRotation = RotationAngle.Rotate90; break; case RotationAngle.Rotate270: currentRotation = RotationAngle.Rotate180; break; } UpdateVisualRotation(); } /// /// 获取当前旋转后的宽度 /// private int GetCurrentWidth() { // 90度和270度时,宽度和高度交换 return (currentRotation == RotationAngle.Rotate90 || currentRotation == RotationAngle.Rotate270) ? _blockShapeData.height : _blockShapeData.width; } /// /// 获取当前旋转后的高度 /// private int GetCurrentHeight() { // 90度和270度时,宽度和高度交换 return (currentRotation == RotationAngle.Rotate90 || currentRotation == RotationAngle.Rotate270) ? _blockShapeData.width : _blockShapeData.height; } /// /// 旋转pattern数组 /// private int[,] RotatePattern(int[,] pattern, RotationAngle angle) { if (pattern == null) return null; int originalWidth = pattern.GetLength(0); int originalHeight = pattern.GetLength(1); int[,] rotatedPattern; switch (angle) { case RotationAngle.Rotate90: // 顺时针90度:new[i][j] = old[width-1-j][i] rotatedPattern = new int[originalHeight, originalWidth]; for (int i = 0; i < originalHeight; i++) { for (int j = 0; j < originalWidth; j++) { rotatedPattern[i, j] = pattern[originalWidth - 1 - j, i]; } } break; case RotationAngle.Rotate180: // 180度:new[i][j] = old[width-1-i][height-1-j] rotatedPattern = new int[originalWidth, originalHeight]; for (int i = 0; i < originalWidth; i++) { for (int j = 0; j < originalHeight; j++) { rotatedPattern[i, j] = pattern[originalWidth - 1 - i, originalHeight - 1 - j]; } } break; case RotationAngle.Rotate270: // 逆时针90度(顺时针270度):new[i][j] = old[j][height-1-i] rotatedPattern = new int[originalHeight, originalWidth]; for (int i = 0; i < originalHeight; i++) { for (int j = 0; j < originalWidth; j++) { rotatedPattern[i, j] = pattern[j, originalHeight - 1 - i]; } } break; default: // Rotate0 rotatedPattern = (int[,])pattern.Clone(); break; } return rotatedPattern; } /// /// 更新视觉旋转(更新transform的rotation) /// private void UpdateVisualRotation() { float rotationZ = (float)currentRotation; transform.rotation = Quaternion.Euler(0, 0, rotationZ); } 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[_blockShapeData.width, _blockShapeData.height]; var defaultSprite = CreateDefaultSprite(); for (int i = 0; i < _blockShapeData.width; i++) { for (int j = 0; j < _blockShapeData.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 * _blockShapeData.cellSize; var cellPosY = j * _blockShapeData.cellSize; cell.transform.localPosition = new Vector3(cellPosX, cellPosY, 0); } } // 反序列化并处理 var pattern = CommonUtil.DeserializeIntArray(_blockShapeData.originalPatternStr); // 先判断pattern的维度是否与width和height一致 if (pattern.GetLength(0) != _blockShapeData.width || pattern.GetLength(1) != _blockShapeData.height) { // 如果不一致,就不处理 Debug.LogWarning($"Pattern dimension mismatch: {pattern.GetLength(0)}x{pattern.GetLength(1)} != {_blockShapeData.width}x{_blockShapeData.height}"); return; } // 如果一致,就读取pattern,并设置对应的cell的SpriteRenderer是否激活 for (int i = 0; i < _blockShapeData.width; i++) { for (int j = 0; j < _blockShapeData.height; j++) { _cellRenderers[i, j].enabled = pattern[i, j] != 0; } } LoadSprite(); // 更新碰撞体形状 UpdatePolygonCollider(); } private void LoadSprite() { if (!string.IsNullOrEmpty(_blockShapeData.spritePath)) { var sprite = ResourceKit.LoadAssetSync(_blockShapeData.spritePath); var renderer = GetComponent(); renderer.sprite = sprite; } } public void SaveShape() { // 如果_cellRenderers不为空,则序列化并保存 if (_cellRenderers != null) { var pattern = new int[_blockShapeData.width, _blockShapeData.height]; for (int i = 0; i < _blockShapeData.width; i++) { for (int j = 0; j < _blockShapeData.height; j++) { pattern[i, j] = _cellRenderers[i, j].enabled ? 1 : 0; } } Debug.Log($"SaveShape: {CommonUtil.SerializeIntArray(pattern)}"); _blockShapeData.originalPatternStr = CommonUtil.SerializeIntArray(pattern); var renderer = GetComponent(); if (renderer != null && renderer.sprite != null) { Debug.Log($"SaveShape: {renderer.sprite.name}"); if (ResourceKit.GetAssetAddressableKey(renderer.sprite, out string spritePath)) { _blockShapeData.spritePath = spritePath; } } } BlockPuzzleKit.SaveBlockShapeData(_blockShapeData, _blockShapeData.shapeName); // 保存后更新碰撞体 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 || _blockShapeData.width <= 0 || _blockShapeData.height <= 0 || _blockShapeData.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 <= _blockShapeData.width; i++) { for (int j = 0; j <= _blockShapeData.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) * _blockShapeData.cellSize; float y = (j - 0.5f) * _blockShapeData.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 < _blockShapeData.width && cornerY > 0) ? pattern[cornerX, cornerY - 1] != 0 : false; bool bottomLeft = (cornerX > 0 && cornerY < _blockShapeData.height) ? pattern[cornerX - 1, cornerY] != 0 : false; bool bottomRight = (cornerX < _blockShapeData.width && cornerY < _blockShapeData.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 = _blockShapeData.width, maxX = -1, minY = _blockShapeData.height, maxY = -1; bool hasValidCell = false; for (int i = 0; i < _blockShapeData.width; i++) { for (int j = 0; j < _blockShapeData.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 = _blockShapeData.cellSize / 2f; float minXWorld = minX * _blockShapeData.cellSize - _blockShapeData.width * _blockShapeData.cellSize / 2f - halfSize; float maxXWorld = (maxX + 1) * _blockShapeData.cellSize - _blockShapeData.width * _blockShapeData.cellSize / 2f + halfSize; float minYWorld = minY * _blockShapeData.cellSize - _blockShapeData.height * _blockShapeData.cellSize / 2f - halfSize; float maxYWorld = (maxY + 1) * _blockShapeData.cellSize - _blockShapeData.height * _blockShapeData.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; } } [Serializable] public struct BlockShapeData { public int shapeId; public string shapeName; public int width; public int height; public float cellSize; public string originalPatternStr; public string spritePath; } }