using System.Collections.Generic; using AibisDream.Utility; using UnityEngine; using UnityEngine.Rendering; using AibisDream.Framework; namespace AibisDream { public class BlockShape : MonoBehaviour, IBlockShape { private const string EditorPreviewContainerName = "EditorPreview"; private const string VisualInstanceName = "Visual"; private SpriteRenderer[,] _cellRenderers; private GameObject _visualInstance; private SortingGroup _sortingGroup; public SortingGroup SortingGroup => _sortingGroup; // 模块数据 [Header("模块数据")] [SerializeField] private BlockShapeData _blockShapeData; private RotationAngle currentRotation; // 接口实现 public BlockShapeData BlockShapeData => _blockShapeData; 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; gameObject.name = blockShapeData.shapeName; LoadVisualPrefab(); ShapeDragger.IdleLayerOrder = _sortingGroup != null ? _sortingGroup.sortingOrder : 0; // 更新碰撞体 UpdatePolygonCollider(); } public Vector2Int[] GetBlockPositions() { var pattern = GetCurrentPattern(); if (pattern == null) return new Vector2Int[0]; int currentWidth = GetCurrentWidth(); int currentHeight = GetCurrentHeight(); // 获取根单元位置(原始pattern的(0,0)在旋转后pattern中的位置) Vector2Int rootCell = GetCurrentRootCell(); List positions = new List(); for (int i = 0; i < currentWidth; i++) { for (int j = 0; j < currentHeight; j++) { if (pattern[i, j] != 0) { // 将pattern中的坐标转换为以根单元为(0,0)的坐标系统 // 原来在pattern中的位置(i, j),转换为新坐标系统:(i - rootX, j - rootY) positions.Add(new Vector2Int(i - rootCell.x, j - rootCell.y)); } } } 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); } /// /// 获取当前旋转后根单元的位置 /// 根单元是指原始pattern的(0,0)位置在旋转后的pattern中的位置 /// Rotate0: 左下角 (0, 0) /// Rotate90: 右下角 (height-1, 0) /// Rotate180: 右上角 (width-1, height-1) /// Rotate270: 左上角 (0, width-1) /// /// 根单元在旋转后pattern中的坐标 private Vector2Int GetCurrentRootCell() { int width = _blockShapeData.width; int height = _blockShapeData.height; return currentRotation switch { RotationAngle.Rotate0 => new Vector2Int(0, 0), // 左下角 RotationAngle.Rotate90 => new Vector2Int(height - 1, 0), // 右下角(旋转后pattern尺寸为height x width) RotationAngle.Rotate180 => new Vector2Int(width - 1, height - 1), // 右上角 RotationAngle.Rotate270 => new Vector2Int(0, width - 1), // 左上角(旋转后pattern尺寸为height x width) _ => new Vector2Int(0, 0), }; } 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 Vector3 GetGlobalCenterPosition() { return transform.position + GetCenterPosition(); } 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() { // 返回旋转后坐标轴的翻转方向 // 用于将旋转后pattern的索引坐标转换为网格坐标 // 注意:这里的旋转方向与Unity的transform.rotation一致(逆时针为正方向) 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数组 /// 注意:Unity的旋转是逆时针为正方向,所以这里的旋转方向与Unity的transform.rotation一致 /// 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度(Unity正方向):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; 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: // 逆时针270度(顺时针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; default: // Rotate0 rotatedPattern = (int[,])pattern.Clone(); break; } return rotatedPattern; } public bool CheckExtraLimits(Vector2Int gridRootCell) { if (!_blockShapeData.extraLimits) return true; if (currentRotation != _blockShapeData.rotationAngleLimit) return false; if (gridRootCell != _blockShapeData.rootLimit) return false; return true; } /// /// 更新视觉旋转(更新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() { // 只销毁 EditorPreview 容器,保留 visualInstance var editorPreview = transform.Find(EditorPreviewContainerName); if (editorPreview != null) { DestroyImmediate(editorPreview.gameObject); } _cellRenderers = null; // 创建 EditorPreview 容器 var container = new GameObject(EditorPreviewContainerName); container.transform.SetParent(transform); container.transform.localPosition = Vector3.zero; container.transform.localRotation = Quaternion.identity; container.transform.localScale = Vector3.one; // 在容器下创建 CellRenderers _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.SetParent(container.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}"); } else { // 如果一致,就读取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; } } } LoadVisualPrefab(); ShapeDragger.IdleLayerOrder = _sortingGroup != null ? _sortingGroup.sortingOrder : 0; // 更新碰撞体形状 UpdatePolygonCollider(); } private void LoadVisualPrefab() { // 销毁旧的视觉实例 if (_visualInstance != null) { DestroyImmediate(_visualInstance); _visualInstance = null; _sortingGroup = null; } if (!string.IsNullOrEmpty(_blockShapeData.prefabPath)) { var prefab = ResourceKit.LoadAssetSync(_blockShapeData.prefabPath); if (prefab != null) { _visualInstance = Instantiate(prefab, transform); _visualInstance.name = VisualInstanceName; // 保持 Prefab 中设置的 localPosition,不强制重置 // 这样美术可以在 Prefab 中直接调整位置偏移 _visualInstance.transform.localRotation = Quaternion.identity; // 获取或添加 SortingGroup _sortingGroup = _visualInstance.GetComponent(); if (_sortingGroup == null) { _sortingGroup = _visualInstance.AddComponent(); } } } } #if UNITY_EDITOR 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); } // 保存 visualInstance 对应的 prefab 路径 if (_visualInstance != null) { // 尝试获取 prefab 的 addressable key var prefabSource = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(_visualInstance); // if (prefabSource != null && EditorKit.GetAssetAddressableKey(prefabSource, out string prefabPath)) // { // Debug.Log($"SaveShape prefabPath: {prefabPath}"); // _blockShapeData.prefabPath = prefabPath; // } } BlockPuzzleKit.SaveBlockShapeData(_blockShapeData, _blockShapeData.shapeName); // 保存后更新碰撞体 UpdatePolygonCollider(); } public void LoadShape() { // 根据shapeName加载shape if (BlockPuzzleKit.TryLoadBlockShapeData(_blockShapeData.shapeName, out var shapeData)) { _blockShapeData = shapeData; DrawShape(); } else { Debug.LogWarning($"LoadShape: {_blockShapeData.shapeName} not found"); } } #endif /// /// 根据originalPattern更新PolygonCollider2D的形状 /// public void UpdatePolygonCollider() { var pattern = GetOriginalPattern(); if (pattern == null) return; // 获取或添加PolygonCollider2D组件 PolygonCollider2D polygonCollider = GetComponent(); if (polygonCollider == null) { polygonCollider = gameObject.AddComponent(); } // 使用GridDrawer生成多边形顶点 Vector2[] gridVertices = BlockPuzzleKit.GridToPolygonVertices(pattern); if (gridVertices != null && gridVertices.Length > 0) { // 转换坐标:GridDrawer使用边长2,需要转换为BlockShape的cellSize // GridDrawer坐标系统:格子(i,j)的中心为(i*2, j*2),角点为(i*2-1, j*2-1)等 // BlockShape坐标系统:格子(i,j)的中心为((i+0.5)*cellSize, (j+0.5)*cellSize) // 转换公式:BlockShape坐标 = GridDrawer坐标 * (cellSize / 2) float scale = _blockShapeData.cellSize / 2f; List vertices = new List(); for (int i = 0; i < gridVertices.Length; i++) { vertices.Add(new Vector2(gridVertices[i].x * scale, gridVertices[i].y * scale)); } // 设置多边形路径 polygonCollider.pathCount = 1; polygonCollider.SetPath(0, vertices.ToArray()); } else { // 如果没有有效格子,清除路径 polygonCollider.pathCount = 0; } } public void SnapToGrid() { var grid = FindFirstObjectByType(); if (grid == null) { Debug.LogWarning("无法找到BlockPuzzleGrid"); return; } var snapResult = grid.CheckSnapToGrid(this); if (snapResult.canSnap) { grid.TrySnapToGrid(this, snapResult.RootCell); } else { Debug.LogWarning("无法吸附到网格"); } } public void DeleteShape() { var grid = FindFirstObjectByType(); if (grid != null) { grid.ShapeDict.Remove(this); DestroyImmediate(gameObject); } } } }