方块拼图
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0eb5cc55dcdc0f244a5cb635090c0bd3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+112
@@ -14,6 +14,13 @@ namespace AibisDream
|
||||
[SerializeField] private float cellSize = 1f;
|
||||
[SerializeField] private bool initializeOnStart = true;
|
||||
|
||||
[Header("编辑器预览设置")]
|
||||
[SerializeField] private bool showGizmos = true;
|
||||
[SerializeField] private Color gridLineColor = new Color(0.5f, 0.5f, 0.5f, 0.8f);
|
||||
[SerializeField] private Color emptyCellColor = new Color(0.9f, 0.9f, 0.9f, 0.2f);
|
||||
[SerializeField] private Color occupiedCellColor = new Color(0.2f, 0.6f, 1f, 0.5f);
|
||||
[SerializeField] private bool showCellStates = true;
|
||||
|
||||
private GridCellState[,] _grid;
|
||||
private bool _isInitialized = false;
|
||||
|
||||
@@ -157,6 +164,24 @@ namespace AibisDream
|
||||
return new Vector3(worldX, worldY, Origin.z);
|
||||
}
|
||||
|
||||
public Vector3[] GridVerticesToWorld(int x, int y)
|
||||
{
|
||||
if (!_isInitialized)
|
||||
{
|
||||
return new Vector3[4];
|
||||
}
|
||||
|
||||
// 先获取网格中心位置
|
||||
Vector3 center = GridToWorld(x, y);
|
||||
// 然后获取网格四个顶点位置
|
||||
Vector3[] vertices = new Vector3[4];
|
||||
vertices[0] = center + new Vector3(-CellSize / 2, -CellSize / 2, 0);
|
||||
vertices[1] = center + new Vector3(CellSize / 2, -CellSize / 2, 0);
|
||||
vertices[2] = center + new Vector3(CellSize / 2, CellSize / 2, 0);
|
||||
vertices[3] = center + new Vector3(-CellSize / 2, CellSize / 2, 0);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (!_isInitialized || _grid == null) return;
|
||||
@@ -209,6 +234,93 @@ namespace AibisDream
|
||||
height = Mathf.Max(1, height);
|
||||
cellSize = Mathf.Max(0.1f, cellSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在编辑器中绘制网格预览
|
||||
/// </summary>
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (!showGizmos) return;
|
||||
|
||||
// 使用当前Inspector中的参数或已初始化的参数
|
||||
int drawWidth = _isInitialized ? Width : width;
|
||||
int drawHeight = _isInitialized ? Height : height;
|
||||
float drawCellSize = _isInitialized ? CellSize : cellSize;
|
||||
Vector3 drawOrigin = _isInitialized ? Origin : transform.position;
|
||||
|
||||
if (drawWidth <= 0 || drawHeight <= 0 || drawCellSize <= 0) return;
|
||||
|
||||
// 绘制网格线
|
||||
Gizmos.color = gridLineColor;
|
||||
for (int x = 0; x <= drawWidth; x++)
|
||||
{
|
||||
Vector3 start = drawOrigin + new Vector3(x * drawCellSize, 0, 0);
|
||||
Vector3 end = drawOrigin + new Vector3(x * drawCellSize, drawHeight * drawCellSize, 0);
|
||||
Gizmos.DrawLine(start, end);
|
||||
}
|
||||
|
||||
for (int y = 0; y <= drawHeight; y++)
|
||||
{
|
||||
Vector3 start = drawOrigin + new Vector3(0, y * drawCellSize, 0);
|
||||
Vector3 end = drawOrigin + new Vector3(drawWidth * drawCellSize, y * drawCellSize, 0);
|
||||
Gizmos.DrawLine(start, end);
|
||||
}
|
||||
|
||||
// 绘制单元格状态(如果已初始化且启用)
|
||||
if (showCellStates && _isInitialized && _grid != null)
|
||||
{
|
||||
float cellDisplaySize = drawCellSize * 0.9f; // 稍微小一点,留出网格线空间
|
||||
Vector3 cellSizeVec = new Vector3(cellDisplaySize, cellDisplaySize, 0.01f);
|
||||
|
||||
for (int x = 0; x < drawWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < drawHeight; y++)
|
||||
{
|
||||
if (IsValidPosition(x, y))
|
||||
{
|
||||
Vector3 cellCenter = GridToWorld(x, y);
|
||||
GridCellState state = GetCellState(x, y);
|
||||
|
||||
Gizmos.color = state == GridCellState.Occupied ? occupiedCellColor : emptyCellColor;
|
||||
Gizmos.DrawCube(cellCenter, cellSizeVec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选中时绘制更明显的网格边界
|
||||
/// </summary>
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!showGizmos) return;
|
||||
|
||||
// 使用当前Inspector中的参数或已初始化的参数
|
||||
int drawWidth = _isInitialized ? Width : width;
|
||||
int drawHeight = _isInitialized ? Height : height;
|
||||
float drawCellSize = _isInitialized ? CellSize : cellSize;
|
||||
Vector3 drawOrigin = _isInitialized ? Origin : transform.position;
|
||||
|
||||
if (drawWidth <= 0 || drawHeight <= 0 || drawCellSize <= 0) return;
|
||||
|
||||
// 绘制网格边界(更粗的线)
|
||||
Gizmos.color = new Color(gridLineColor.r, gridLineColor.g, gridLineColor.b, 1f);
|
||||
Vector3 bottomLeft = drawOrigin;
|
||||
Vector3 bottomRight = drawOrigin + new Vector3(drawWidth * drawCellSize, 0, 0);
|
||||
Vector3 topLeft = drawOrigin + new Vector3(0, drawHeight * drawCellSize, 0);
|
||||
Vector3 topRight = drawOrigin + new Vector3(drawWidth * drawCellSize, drawHeight * drawCellSize, 0);
|
||||
|
||||
// 绘制边界框
|
||||
Gizmos.DrawLine(bottomLeft, bottomRight);
|
||||
Gizmos.DrawLine(bottomRight, topRight);
|
||||
Gizmos.DrawLine(topRight, topLeft);
|
||||
Gizmos.DrawLine(topLeft, bottomLeft);
|
||||
|
||||
// 绘制原点标记
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(drawOrigin, 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
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<Vector2Int> positions = new List<Vector2Int>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取单元格渲染器数组(供拖拽组件使用)
|
||||
/// </summary>
|
||||
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<SpriteRenderer>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 || width <= 0 || height <= 0 || 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 <= 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);
|
||||
}
|
||||
|
||||
/// <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 < 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;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成简单的包围盒(备用方法)
|
||||
/// </summary>
|
||||
private List<Vector2> GenerateBoundingBox(int[,] pattern)
|
||||
{
|
||||
List<Vector2> vertices = new List<Vector2>();
|
||||
|
||||
// 找到有效格子的边界
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4be33b2e5baf96447a7df0d10cb22064
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,405 @@
|
||||
using System;
|
||||
using AibisDream.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
/// <summary>
|
||||
/// 形状拖拽组件
|
||||
/// 实现IBlockShapeDragger接口,处理形状的拖拽、吸附和放置逻辑
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(BlockShape))]
|
||||
[RequireComponent(typeof(EventTriggerEx))]
|
||||
public class BlockShapeDragger : MonoBehaviour, IBlockShapeDragger
|
||||
{
|
||||
[Header("拖拽设置")]
|
||||
[SerializeField] private bool isDraggable = true;
|
||||
[SerializeField] private float snapDistance = 0.5f;
|
||||
[SerializeField] private BlockPuzzleGrid targetGridComponent; // Unity组件引用
|
||||
|
||||
[Header("视觉反馈")]
|
||||
[SerializeField] private Color snapColor = new Color(0f, 1f, 0f, 0.7f);
|
||||
[SerializeField] private Color invalidColor = new Color(1f, 0f, 0f, 0.7f);
|
||||
[SerializeField] private Color normalColor = Color.white;
|
||||
[SerializeField] private float dragScale = 1.1f;
|
||||
|
||||
// 拖拽状态
|
||||
private DragState _currentState = DragState.Idle;
|
||||
private bool _isDragging = false;
|
||||
private Vector3 _dragOffset;
|
||||
private Vector3 _originalPosition;
|
||||
private Vector3 _originalScale;
|
||||
private int _originalSortingOrder;
|
||||
private bool _isSnapping = false;
|
||||
private int _snapGridX;
|
||||
private int _snapGridY;
|
||||
|
||||
// 组件引用
|
||||
private BlockShape _blockShape;
|
||||
private SpriteRenderer _spriteRenderer;
|
||||
private Collider2D _collider2D;
|
||||
|
||||
// 事件
|
||||
public event Action<IBlockShape> OnDragStart;
|
||||
public event Action<IBlockShape, Vector3> OnDragUpdate;
|
||||
public event Action<IBlockShape, bool> OnDragEnd;
|
||||
public event Action<IBlockShape, Vector3, int, int> OnSnap;
|
||||
public event Action<IBlockShape> OnUnsnap;
|
||||
|
||||
// 接口实现
|
||||
public DragState CurrentState => _currentState;
|
||||
public bool IsDraggable { get => isDraggable; set => isDraggable = value; }
|
||||
public IBlockPuzzleGrid TargetGrid
|
||||
{
|
||||
get => targetGridComponent;
|
||||
set => targetGridComponent = value as BlockPuzzleGrid;
|
||||
}
|
||||
public float SnapDistance { get => snapDistance; set => snapDistance = value; }
|
||||
|
||||
// 内部使用的网格引用
|
||||
private IBlockPuzzleGrid targetGrid => targetGridComponent;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_blockShape = GetComponent<BlockShape>();
|
||||
_spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
}
|
||||
|
||||
public void StartDrag()
|
||||
{
|
||||
if (!isDraggable || _isDragging) return;
|
||||
|
||||
_isDragging = true;
|
||||
_currentState = DragState.Dragging;
|
||||
_originalPosition = transform.position;
|
||||
_originalScale = transform.localScale;
|
||||
|
||||
// 计算拖拽偏移量
|
||||
Vector3 mouseWorldPos = GetMouseWorldPosition();
|
||||
_dragOffset = transform.position - mouseWorldPos;
|
||||
|
||||
// 提升层级和缩放
|
||||
if (_spriteRenderer != null)
|
||||
{
|
||||
_originalSortingOrder = _spriteRenderer.sortingOrder;
|
||||
_spriteRenderer.sortingOrder = 100;
|
||||
}
|
||||
|
||||
transform.localScale = _originalScale * dragScale;
|
||||
|
||||
OnDragStart?.Invoke(_blockShape);
|
||||
}
|
||||
|
||||
public void UpdateDrag()
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
Vector3 mouseWorldPos = GetMouseWorldPosition();
|
||||
Vector3 targetPosition = mouseWorldPos + _dragOffset;
|
||||
|
||||
// 检测是否靠近网格
|
||||
if (targetGrid != null)
|
||||
{
|
||||
bool canSnap = CheckSnapToGrid(targetPosition, out Vector3 snapPos, out int gridX, out int gridY);
|
||||
|
||||
if (canSnap)
|
||||
{
|
||||
// 吸附到网格
|
||||
transform.position = snapPos;
|
||||
_isSnapping = true;
|
||||
_snapGridX = gridX;
|
||||
_snapGridY = gridY;
|
||||
_currentState = DragState.Snapping;
|
||||
|
||||
// 重要:更新拖拽偏移量,使鼠标继续移动时能正确计算位置
|
||||
_dragOffset = snapPos - mouseWorldPos;
|
||||
|
||||
// 检查是否可以放置
|
||||
bool canPlace = CanPlaceAt(gridX, gridY);
|
||||
ShowSnapPreview(true, canPlace);
|
||||
|
||||
OnSnap?.Invoke(_blockShape, snapPos, gridX, gridY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 自由拖拽
|
||||
transform.position = targetPosition;
|
||||
if (_isSnapping)
|
||||
{
|
||||
_isSnapping = false;
|
||||
ShowSnapPreview(false, false);
|
||||
OnUnsnap?.Invoke(_blockShape);
|
||||
}
|
||||
_currentState = DragState.Dragging;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有网格,自由拖拽
|
||||
transform.position = targetPosition;
|
||||
_currentState = DragState.Dragging;
|
||||
}
|
||||
|
||||
OnDragUpdate?.Invoke(_blockShape, transform.position);
|
||||
}
|
||||
|
||||
public void EndDrag()
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
bool placed = false;
|
||||
|
||||
if (_isSnapping && targetGrid != null)
|
||||
{
|
||||
// 尝试放置到网格
|
||||
if (CanPlaceAt(_snapGridX, _snapGridY))
|
||||
{
|
||||
if (TryPlaceOnGrid(_snapGridX, _snapGridY))
|
||||
{
|
||||
placed = true;
|
||||
_currentState = DragState.Placed;
|
||||
OnShapePlaced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed)
|
||||
{
|
||||
// 返回原位置
|
||||
transform.position = _originalPosition;
|
||||
_currentState = DragState.Idle;
|
||||
}
|
||||
|
||||
// 恢复原始状态
|
||||
transform.localScale = _originalScale;
|
||||
if (_spriteRenderer != null)
|
||||
{
|
||||
_spriteRenderer.sortingOrder = _originalSortingOrder;
|
||||
}
|
||||
|
||||
ShowSnapPreview(false, false);
|
||||
|
||||
_isDragging = false;
|
||||
_isSnapping = false;
|
||||
|
||||
OnDragEnd?.Invoke(_blockShape, placed);
|
||||
}
|
||||
|
||||
public void CancelDrag()
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
transform.position = _originalPosition;
|
||||
transform.localScale = _originalScale;
|
||||
if (_spriteRenderer != null)
|
||||
{
|
||||
_spriteRenderer.sortingOrder = _originalSortingOrder;
|
||||
}
|
||||
|
||||
ShowSnapPreview(false, false);
|
||||
|
||||
_isDragging = false;
|
||||
_isSnapping = false;
|
||||
_currentState = DragState.Idle;
|
||||
|
||||
OnDragEnd?.Invoke(_blockShape, false);
|
||||
}
|
||||
|
||||
public bool CanPlaceAt(int gridX, int gridY)
|
||||
{
|
||||
if (targetGrid == null || _blockShape == null) return false;
|
||||
|
||||
Vector2Int[] blockPositions = _blockShape.GetBlockPositions();
|
||||
if (blockPositions == null || blockPositions.Length == 0) return false;
|
||||
|
||||
// 形状的坐标系统:
|
||||
// - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角
|
||||
// - gridX, gridY 是形状左下角在网格中的位置
|
||||
// - 需要将形状的相对坐标转换为网格坐标
|
||||
foreach (var blockPos in blockPositions)
|
||||
{
|
||||
// 将形状的相对坐标转换为网格坐标(左下角为基准)
|
||||
// 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1)
|
||||
// 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j)
|
||||
int checkX = gridX + blockPos.x;
|
||||
int checkY = gridY + _blockShape.Height - 1 - blockPos.y;
|
||||
|
||||
if (!targetGrid.IsValidPosition(checkX, checkY) ||
|
||||
!targetGrid.IsCellEmpty(checkX, checkY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetSnapGridPosition(out int gridX, out int gridY)
|
||||
{
|
||||
if (_isSnapping)
|
||||
{
|
||||
gridX = _snapGridX;
|
||||
gridY = _snapGridY;
|
||||
return true;
|
||||
}
|
||||
|
||||
gridX = 0;
|
||||
gridY = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否可以吸附到网格
|
||||
/// </summary>
|
||||
private bool CheckSnapToGrid(Vector3 worldPos, out Vector3 snapPos, out int gridX, out int gridY)
|
||||
{
|
||||
snapPos = worldPos;
|
||||
gridX = 0;
|
||||
gridY = 0;
|
||||
|
||||
if (targetGrid == null) return false;
|
||||
|
||||
// 将世界坐标转换为网格坐标
|
||||
// 注意:WorldToGrid 需要世界坐标在网格范围内才能返回true
|
||||
// 但我们需要检查所有附近的网格单元格,不仅仅是当前所在的单元格
|
||||
// 所以先尝试直接转换,如果失败则查找最近的网格单元格
|
||||
|
||||
int tempGridX, tempGridY;
|
||||
bool inGrid = targetGrid.WorldToGrid(worldPos, out tempGridX, out tempGridY);
|
||||
|
||||
if (!inGrid)
|
||||
{
|
||||
// 如果不在网格内,尝试查找最近的网格单元格
|
||||
// 计算最近的网格坐标
|
||||
Vector3 localPos = worldPos - targetGrid.Origin;
|
||||
tempGridX = Mathf.FloorToInt(localPos.x / targetGrid.CellSize);
|
||||
tempGridY = Mathf.FloorToInt(localPos.y / targetGrid.CellSize);
|
||||
|
||||
// 检查是否在有效范围内
|
||||
if (!targetGrid.IsValidPosition(tempGridX, tempGridY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取网格单元格中心的世界坐标
|
||||
Vector3 gridCenter = targetGrid.GridToWorld(tempGridX, tempGridY);
|
||||
|
||||
// 计算距离
|
||||
float distance = Vector3.Distance(worldPos, gridCenter);
|
||||
|
||||
if (distance <= snapDistance)
|
||||
{
|
||||
// 计算形状应该放置的网格位置
|
||||
// 形状左下角对齐到网格单元格左下角
|
||||
gridX = tempGridX;
|
||||
gridY = tempGridY;
|
||||
|
||||
// 计算形状左下角的世界坐标
|
||||
// 网格单元格左下角的世界坐标 = Origin + (gridX * CellSize, gridY * CellSize)
|
||||
Vector3 gridBottomLeft = targetGrid.Origin + new Vector3(
|
||||
gridX * targetGrid.CellSize,
|
||||
gridY * targetGrid.CellSize,
|
||||
0
|
||||
);
|
||||
|
||||
// 计算形状中心相对于形状左下角的偏移
|
||||
// 形状的 pattern 中,(0, 0) 是左上角,所以形状左下角在 pattern 中的相对位置是 (0, height-1)
|
||||
// 在形状的本地坐标系中(假设形状中心在 (0, 0)),形状左下角的位置是:
|
||||
// localX = -width * cellSize / 2
|
||||
// localY = -height * cellSize / 2 + (height-1) * cellSize = (height-1) * cellSize - height * cellSize / 2
|
||||
// 形状中心相对于形状左下角的偏移 = (0, 0) - (localX, localY) = (width * cellSize / 2, height * cellSize / 2 - (height-1) * cellSize)
|
||||
|
||||
// 为了简化,我们假设形状的中心在形状的几何中心
|
||||
// 形状中心相对于形状左下角的偏移 = (width * cellSize / 2, height * cellSize / 2)
|
||||
// 但考虑到形状的 pattern 中 (0, 0) 是左上角,形状左下角在 pattern 中的位置是 (0, height-1)
|
||||
// 所以形状中心相对于形状左下角的偏移 = (width * cellSize / 2, (height-1) * cellSize / 2)
|
||||
|
||||
// 实际上,我们需要根据形状的实际布局计算形状中心的位置
|
||||
// 但为了简化,我们直接使用网格单元格左下角作为形状左下角的位置
|
||||
// 然后根据形状的实际布局计算形状中心的位置
|
||||
|
||||
// 形状左下角对齐到网格单元格左下角
|
||||
// 形状中心的世界坐标 = 网格单元格左下角的世界坐标 + 形状中心相对于形状左下角的偏移
|
||||
// 使用网格的 CellSize,因为形状的 cellSize 应该与网格的 CellSize 相同
|
||||
float shapeCenterOffsetX = _blockShape.Width * targetGrid.CellSize / 2f;
|
||||
float shapeCenterOffsetY = (_blockShape.Height - 1) * targetGrid.CellSize / 2f;
|
||||
snapPos = gridBottomLeft + new Vector3(shapeCenterOffsetX, shapeCenterOffsetY, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试放置到网格
|
||||
/// </summary>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
private bool TryPlaceOnGrid(int gridX, int gridY)
|
||||
{
|
||||
if (!CanPlaceAt(gridX, gridY)) return false;
|
||||
|
||||
Vector2Int[] blockPositions = _blockShape.GetBlockPositions();
|
||||
if (blockPositions == null) return false;
|
||||
|
||||
// 形状的坐标系统:
|
||||
// - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角
|
||||
// - gridX, gridY 是形状左下角在网格中的位置
|
||||
// - 需要将形状的相对坐标转换为网格坐标
|
||||
foreach (var blockPos in blockPositions)
|
||||
{
|
||||
// 将形状的相对坐标转换为网格坐标(左下角为基准)
|
||||
// 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1)
|
||||
// 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j)
|
||||
int x = gridX + blockPos.x;
|
||||
int y = gridY + _blockShape.Height - 1 - blockPos.y;
|
||||
targetGrid.SetCellState(x, y, GridCellState.Occupied);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示吸附预览效果
|
||||
/// </summary>
|
||||
private void ShowSnapPreview(bool show, bool isValid)
|
||||
{
|
||||
Color previewColor = show ? (isValid ? snapColor : invalidColor) : normalColor;
|
||||
|
||||
// 更新主SpriteRenderer
|
||||
if (_spriteRenderer != null)
|
||||
{
|
||||
_spriteRenderer.color = previewColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 形状放置完成
|
||||
/// </summary>
|
||||
private void OnShapePlaced()
|
||||
{
|
||||
// 可以在这里添加放置后的逻辑
|
||||
// 例如:禁用拖拽、播放动画等
|
||||
isDraggable = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取鼠标世界坐标
|
||||
/// </summary>
|
||||
private Vector3 GetMouseWorldPosition()
|
||||
{
|
||||
return CameraKit.GetMouseWorldPos(Input.mousePosition);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
CancelDrag();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ad01079e32d2314eb3719ead5f50ddb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+4
-4
@@ -135,8 +135,8 @@ namespace AibisDream
|
||||
for (int x = 0; x <= _grid.Width; x++)
|
||||
{
|
||||
CreateLine(
|
||||
_grid.GridToWorld(x, 0),
|
||||
_grid.GridToWorld(x, _grid.Height),
|
||||
_grid.GridVerticesToWorld(x, 0)[0],
|
||||
_grid.GridVerticesToWorld(x, _grid.Height)[0],
|
||||
gridLineWidth,
|
||||
gridLineColor
|
||||
);
|
||||
@@ -145,8 +145,8 @@ namespace AibisDream
|
||||
for (int y = 0; y <= _grid.Height; y++)
|
||||
{
|
||||
CreateLine(
|
||||
_grid.GridToWorld(0, y),
|
||||
_grid.GridToWorld(_grid.Width, y),
|
||||
_grid.GridVerticesToWorld(0, y)[0],
|
||||
_grid.GridVerticesToWorld(_grid.Width, y)[0],
|
||||
gridLineWidth,
|
||||
gridLineColor
|
||||
);
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
/// <summary>
|
||||
/// 放置验证器实现
|
||||
/// 负责验证形状是否可以放置在指定位置
|
||||
/// </summary>
|
||||
public class PlacementValidator : IPlacementValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查形状是否可以放置在指定位置
|
||||
/// </summary>
|
||||
/// <param name="grid">网格系统</param>
|
||||
/// <param name="shape">要放置的形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <returns>放置验证结果</returns>
|
||||
public PlacementResult CanPlace(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY)
|
||||
{
|
||||
if (grid == null || shape == null)
|
||||
{
|
||||
return PlacementResult.InvalidPosition;
|
||||
}
|
||||
|
||||
// 获取形状的所有格子位置(相对坐标,基于形状的本地坐标系)
|
||||
Vector2Int[] blockPositions = shape.GetBlockPositions();
|
||||
if (blockPositions == null || blockPositions.Length == 0)
|
||||
{
|
||||
return PlacementResult.InvalidPosition;
|
||||
}
|
||||
|
||||
// 形状的坐标系统:
|
||||
// - GetBlockPositions() 返回的坐标中,(0, 0) 是形状的左上角(在pattern数组中)
|
||||
// - pattern[i, j] 中,i 是 x(从左到右),j 是 y(从上到下,j=0 是顶部)
|
||||
// - gridX, gridY 是形状左下角在网格中的位置
|
||||
// - 需要将形状的相对坐标转换为网格坐标
|
||||
foreach (var blockPos in blockPositions)
|
||||
{
|
||||
// 将形状的相对坐标转换为网格坐标
|
||||
// 形状的 (0, 0) 对应网格的 (gridX, gridY + shape.Height - 1)
|
||||
// 形状的 (i, j) 对应网格的 (gridX + i, gridY + shape.Height - 1 - j)
|
||||
int gridCellX = gridX + blockPos.x;
|
||||
int gridCellY = gridY + shape.Height - 1 - blockPos.y;
|
||||
|
||||
// 检查是否超出边界
|
||||
if (!grid.IsValidPosition(gridCellX, gridCellY))
|
||||
{
|
||||
return PlacementResult.OutOfBounds;
|
||||
}
|
||||
|
||||
// 检查是否与已有块重叠
|
||||
if (!grid.IsCellEmpty(gridCellX, gridCellY))
|
||||
{
|
||||
return PlacementResult.Overlap;
|
||||
}
|
||||
}
|
||||
|
||||
return PlacementResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查形状是否可以放置在指定位置(详细版本)
|
||||
/// </summary>
|
||||
/// <param name="grid">网格系统</param>
|
||||
/// <param name="shape">要放置的形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <param name="failureReason">失败原因(如果返回false)</param>
|
||||
/// <returns>是否可以放置</returns>
|
||||
public bool CanPlaceDetailed(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY, out PlacementResult failureReason)
|
||||
{
|
||||
failureReason = CanPlace(grid, shape, gridX, gridY);
|
||||
return failureReason == PlacementResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取形状在指定位置的所有占用格子坐标
|
||||
/// </summary>
|
||||
/// <param name="shape">形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <returns>占用格子的网格坐标数组</returns>
|
||||
public (int x, int y)[] GetOccupiedCells(IBlockShape shape, int gridX, int gridY)
|
||||
{
|
||||
if (shape == null)
|
||||
{
|
||||
return new (int x, int y)[0];
|
||||
}
|
||||
|
||||
Vector2Int[] blockPositions = shape.GetBlockPositions();
|
||||
if (blockPositions == null || blockPositions.Length == 0)
|
||||
{
|
||||
return new (int x, int y)[0];
|
||||
}
|
||||
|
||||
List<(int x, int y)> occupiedCells = new List<(int x, int y)>();
|
||||
|
||||
foreach (var blockPos in blockPositions)
|
||||
{
|
||||
// 将形状的相对坐标转换为网格坐标(左下角为基准)
|
||||
int gridCellX = gridX + blockPos.x;
|
||||
int gridCellY = gridY + shape.Height - 1 - blockPos.y;
|
||||
occupiedCells.Add((gridCellX, gridCellY));
|
||||
}
|
||||
|
||||
return occupiedCells.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 826c5312f504d944c924549de95babe7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abf557018657fa642a0d79187ed450eb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+6
@@ -117,6 +117,12 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
/// <returns>总单元格数量</returns>
|
||||
int GetTotalCellCount();
|
||||
|
||||
/// <summary>
|
||||
/// 获取网格顶点信息
|
||||
/// </summary>
|
||||
/// <returns>网格四个顶点位置</returns>
|
||||
Vector3[] GridVerticesToWorld(int x, int y);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ namespace AibisDream
|
||||
/// 创建形状的副本
|
||||
/// </summary>
|
||||
/// <returns>形状副本</returns>
|
||||
IBlockShape Clone();
|
||||
// IBlockShape Clone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace AibisDream
|
||||
{
|
||||
/// <summary>
|
||||
/// 拖拽状态
|
||||
/// </summary>
|
||||
public enum DragState
|
||||
{
|
||||
Idle, // 空闲
|
||||
Dragging, // 拖拽中
|
||||
Snapping, // 吸附中
|
||||
Placed // 已放置
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 形状拖拽接口
|
||||
/// 负责处理形状的拖拽、吸附和放置逻辑
|
||||
/// </summary>
|
||||
public interface IBlockShapeDragger
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前拖拽状态
|
||||
/// </summary>
|
||||
DragState CurrentState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否可拖拽
|
||||
/// </summary>
|
||||
bool IsDraggable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标网格
|
||||
/// </summary>
|
||||
IBlockPuzzleGrid TargetGrid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 吸附距离阈值
|
||||
/// </summary>
|
||||
float SnapDistance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽开始事件
|
||||
/// </summary>
|
||||
event Action<IBlockShape> OnDragStart;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽更新事件
|
||||
/// </summary>
|
||||
event Action<IBlockShape, Vector3> OnDragUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽结束事件
|
||||
/// </summary>
|
||||
event Action<IBlockShape, bool> OnDragEnd; // bool表示是否成功放置
|
||||
|
||||
/// <summary>
|
||||
/// 吸附事件
|
||||
/// </summary>
|
||||
event Action<IBlockShape, Vector3, int, int> OnSnap; // 形状, 位置, 网格X, 网格Y
|
||||
|
||||
/// <summary>
|
||||
/// 取消吸附事件
|
||||
/// </summary>
|
||||
event Action<IBlockShape> OnUnsnap;
|
||||
|
||||
/// <summary>
|
||||
/// 开始拖拽
|
||||
/// </summary>
|
||||
void StartDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 更新拖拽位置
|
||||
/// </summary>
|
||||
void UpdateDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 结束拖拽
|
||||
/// </summary>
|
||||
void EndDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 取消拖拽(返回原位置)
|
||||
/// </summary>
|
||||
void CancelDrag();
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否可以放置到指定位置
|
||||
/// </summary>
|
||||
/// <param name="gridX">网格X坐标</param>
|
||||
/// <param name="gridY">网格Y坐标</param>
|
||||
/// <returns>是否可以放置</returns>
|
||||
bool CanPlaceAt(int gridX, int gridY);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前吸附的网格坐标
|
||||
/// </summary>
|
||||
/// <param name="gridX">输出的网格X坐标</param>
|
||||
/// <param name="gridY">输出的网格Y坐标</param>
|
||||
/// <returns>是否正在吸附</returns>
|
||||
bool GetSnapGridPosition(out int gridX, out int gridY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b2cbf04f1d9ef6448d01501a7861a46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+6
-7
@@ -22,8 +22,8 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
/// <param name="grid">网格系统</param>
|
||||
/// <param name="shape">要放置的形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左上角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左上角位置)</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <returns>放置验证结果</returns>
|
||||
PlacementResult CanPlace(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY);
|
||||
|
||||
@@ -32,8 +32,8 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
/// <param name="grid">网格系统</param>
|
||||
/// <param name="shape">要放置的形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左上角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左上角位置)</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <param name="failureReason">失败原因(如果返回false)</param>
|
||||
/// <returns>是否可以放置</returns>
|
||||
bool CanPlaceDetailed(IBlockPuzzleGrid grid, IBlockShape shape, int gridX, int gridY, out PlacementResult failureReason);
|
||||
@@ -42,10 +42,9 @@ namespace AibisDream
|
||||
/// 获取形状在指定位置的所有占用格子坐标
|
||||
/// </summary>
|
||||
/// <param name="shape">形状</param>
|
||||
/// <param name="gridX">网格X坐标(形状左上角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左上角位置)</param>
|
||||
/// <param name="gridX">网格X坐标(形状左下角位置)</param>
|
||||
/// <param name="gridY">网格Y坐标(形状左下角位置)</param>
|
||||
/// <returns>占用格子的网格坐标数组</returns>
|
||||
(int x, int y)[] GetOccupiedCells(IBlockShape shape, int gridX, int gridY);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user