using System; using AibisDream.Framework; using UnityEngine; using UnityEngine.EventSystems; 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 EventTriggerEx _eventTrigger; // 事件 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(); _eventTrigger = GetComponent(); RegisterEvent(); } private void RegisterEvent() { _eventTrigger.Register(EventTriggerType.PointerClick, OnPointerClick); } private void OnPointerClick(BaseEventData eventData) { if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) { return; } PointerEventData pointerEventData = eventData as PointerEventData; Vector3 mouseWorldPos = GetMouseWorldPosition(pointerEventData.position); // 如果当前物体不在拖拽中则进入拖拽逻辑 if (!_isDragging) { StartDrag(mouseWorldPos); } // 如果当前物体在拖拽中则取消拖拽 else { CancelDrag(); } } public void StartDrag(Vector3 mouseWorldPos) { if (!isDraggable || _isDragging) return; _isDragging = true; _currentState = DragState.Dragging; _originalPosition = transform.position; _originalScale = transform.localScale; // 计算拖拽偏移量 _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(Input.mousePosition); Vector3 targetPosition = mouseWorldPos + _dragOffset; if (targetGrid != null) { // 检查吸附可能 var snapResult = targetGrid.CheckSnapToGrid(_blockShape); if (snapResult.canSnap) { // 可以吸附,保存吸附内容 } else { // 没有吸附,自由拖拽 transform.position = targetPosition; _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) // 形状的 (i, j) 对应网格的 (gridX + i, gridY + j) int checkX = gridX + blockPos.x; int checkY = gridY + 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) 是左下角 // 在形状的本地坐标系中(假设形状中心在 (0, 0)),形状左下角的位置是: // localX = -width * cellSize / 2 // localY = -height * cellSize / 2 // 形状中心相对于形状左下角的偏移 = (0, 0) - (localX, localY) = (width * cellSize / 2, height * cellSize / 2) // 形状左下角对齐到网格单元格左下角 // 形状中心的世界坐标 = 网格单元格左下角的世界坐标 + 形状中心相对于形状左下角的偏移 // 使用网格的 CellSize,因为形状的 cellSize 应该与网格的 CellSize 相同 float shapeCenterOffsetX = _blockShape.Width * targetGrid.CellSize / 2f; float shapeCenterOffsetY = _blockShape.Height * 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) // 形状的 (i, j) 对应网格的 (gridX + i, gridY + j) int x = gridX + blockPos.x; int y = gridY + 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(Vector3 mousePos) { var pos = Camera.main.ScreenToWorldPoint(mousePos); return new Vector3(pos.x, pos.y, 0); } private void OnDisable() { if (_isDragging) { CancelDrag(); } } } }