using System; using AibisDream.Framework; using UnityEngine; namespace AibisDream { /// /// 形状拖拽组件 /// 实现IBlockShapeDragger接口,处理形状的拖拽、吸附和放置逻辑 /// [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 OnDragStart; public event Action OnDragUpdate; public event Action OnDragEnd; public event Action OnSnap; public event Action 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(); _spriteRenderer = GetComponent(); } 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; } /// /// 检查是否可以吸附到网格 /// 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; } /// /// 尝试放置到网格 /// /// 网格X坐标(形状左下角位置) /// 网格Y坐标(形状左下角位置) 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; } /// /// 显示吸附预览效果 /// private void ShowSnapPreview(bool show, bool isValid) { Color previewColor = show ? (isValid ? snapColor : invalidColor) : normalColor; // 更新主SpriteRenderer if (_spriteRenderer != null) { _spriteRenderer.color = previewColor; } } /// /// 形状放置完成 /// private void OnShapePlaced() { // 可以在这里添加放置后的逻辑 // 例如:禁用拖拽、播放动画等 isDraggable = false; } /// /// 获取鼠标世界坐标 /// private Vector3 GetMouseWorldPosition() { return CameraKit.GetMouseWorldPos(Input.mousePosition); } private void OnDisable() { if (_isDragging) { CancelDrag(); } } } }