676 lines
25 KiB
C#
676 lines
25 KiB
C#
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<ShapeDragger>();
|
||
_blockShapeData = blockShapeData;
|
||
gameObject.name = blockShapeData.shapeName;
|
||
// 加载Sprite
|
||
LoadSprite();
|
||
// 更新碰撞体
|
||
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<Vector2Int> positions = new List<Vector2Int>();
|
||
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前旋转后根单元的位置
|
||
/// 根单元是指原始pattern的(0,0)位置在旋转后的pattern中的位置
|
||
/// Rotate0: 左下角 (0, 0)
|
||
/// Rotate90: 右下角 (height-1, 0)
|
||
/// Rotate180: 右上角 (width-1, height-1)
|
||
/// Rotate270: 左上角 (0, width-1)
|
||
/// </summary>
|
||
/// <returns>根单元在旋转后pattern中的坐标</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据网格情况计算出形状的物理中心位置
|
||
/// </summary>
|
||
/// <returns>形状的物理中心位置,z为0</returns>
|
||
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()
|
||
{
|
||
// 返回旋转后坐标轴的翻转方向
|
||
// 用于将旋转后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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前旋转后的宽度
|
||
/// </summary>
|
||
private int GetCurrentWidth()
|
||
{
|
||
// 90度和270度时,宽度和高度交换
|
||
return (currentRotation == RotationAngle.Rotate90 || currentRotation == RotationAngle.Rotate270)
|
||
? _blockShapeData.height
|
||
: _blockShapeData.width;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前旋转后的高度
|
||
/// </summary>
|
||
private int GetCurrentHeight()
|
||
{
|
||
// 90度和270度时,宽度和高度交换
|
||
return (currentRotation == RotationAngle.Rotate90 || currentRotation == RotationAngle.Rotate270)
|
||
? _blockShapeData.width
|
||
: _blockShapeData.height;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 旋转pattern数组
|
||
/// 注意:Unity的旋转是逆时针为正方向,所以这里的旋转方向与Unity的transform.rotation一致
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新视觉旋转(更新transform的rotation)
|
||
/// </summary>
|
||
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<SpriteRenderer>();
|
||
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()
|
||
{
|
||
var renderer = GetComponent<SpriteRenderer>();
|
||
if (!string.IsNullOrEmpty(_blockShapeData.spritePath))
|
||
{
|
||
var sprite = ResourceKit.LoadAssetSync<Sprite>(_blockShapeData.spritePath);
|
||
renderer.sprite = sprite;
|
||
}
|
||
else
|
||
{
|
||
renderer.sprite = null;
|
||
}
|
||
}
|
||
|
||
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<SpriteRenderer>();
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据originalPattern更新PolygonCollider2D的形状
|
||
/// </summary>
|
||
public void UpdatePolygonCollider()
|
||
{
|
||
var pattern = GetOriginalPattern();
|
||
if (pattern == null) return;
|
||
|
||
// 获取或添加PolygonCollider2D组件
|
||
PolygonCollider2D polygonCollider = GetComponent<PolygonCollider2D>();
|
||
if (polygonCollider == null)
|
||
{
|
||
polygonCollider = gameObject.AddComponent<PolygonCollider2D>();
|
||
}
|
||
|
||
// 生成多边形顶点
|
||
List<Vector2> vertices = GeneratePolygonVertices(pattern);
|
||
|
||
if (vertices.Count > 0)
|
||
{
|
||
// 设置多边形路径
|
||
polygonCollider.pathCount = 1;
|
||
polygonCollider.SetPath(0, vertices.ToArray());
|
||
}
|
||
else
|
||
{
|
||
// 如果没有有效格子,清除路径
|
||
polygonCollider.pathCount = 0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据pattern生成多边形顶点
|
||
/// </summary>
|
||
private List<Vector2> GeneratePolygonVertices(int[,] pattern)
|
||
{
|
||
List<Vector2> vertices = new List<Vector2>();
|
||
|
||
if (pattern == null || _blockShapeData.width <= 0 || _blockShapeData.height <= 0 || _blockShapeData.cellSize <= 0)
|
||
return vertices;
|
||
|
||
// 生成精确的外轮廓(只包含边界点)
|
||
vertices = GenerateOutlineVertices(pattern);
|
||
|
||
return vertices;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成精确的外轮廓顶点(只包含边界点)
|
||
/// 使用Marching Squares算法的简化版本
|
||
/// </summary>
|
||
private List<Vector2> GenerateOutlineVertices(int[,] pattern)
|
||
{
|
||
List<Vector2> outlineVertices = new List<Vector2>();
|
||
HashSet<Vector2Int> visitedPoints = new HashSet<Vector2Int>();
|
||
|
||
// 遍历所有格子,找到边界点
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查角点是否在边界上
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 生成简单的包围盒(备用方法)
|
||
/// </summary>
|
||
private List<Vector2> GenerateBoundingBox(int[,] pattern)
|
||
{
|
||
List<Vector2> vertices = new List<Vector2>();
|
||
|
||
// 找到有效格子的边界
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对顶点进行排序,形成闭合多边形
|
||
/// </summary>
|
||
private List<Vector2> OrderVerticesForPolygon(List<Vector2> 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;
|
||
}
|
||
|
||
public void SnapToGrid()
|
||
{
|
||
var grid = FindFirstObjectByType<BlockPuzzleGrid>();
|
||
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<BlockPuzzleGrid>();
|
||
if (grid != null)
|
||
{
|
||
grid.ShapeDict.Remove(this);
|
||
DestroyImmediate(gameObject);
|
||
}
|
||
}
|
||
}
|
||
} |