using System; using System.Collections; using AibisDream.Framework; using UnityEngine; using UnityEngine.EventSystems; namespace AibisDream { public class ShapeDragger : MonoBehaviour, IBlockShapeDragger, IInteraction { #region 编辑器字段 [SerializeField] private bool isDraggable = true; [SerializeField] private int dragLayerId; [SerializeField] private int dragLayerOrder; [SerializeField] private int idleLayerId; [SerializeField] private int idleLayerOrder; [Header("长按设置")] [SerializeField] private float longPressDuration = 0.65f; [SerializeField] private float longPressScale = 1.1f; [SerializeField] private float shakeIntensity = 0.1f; #endregion // 拖拽状态 private DragState _currentState = DragState.Placed; public DragState CurrentState { get => _currentState; set { if (value == _currentState) return; // 处理状态切换 HandleStateChange(value); _currentState = value; } } private SnapResult _snapResult; // 长按相关状态 private bool _isPressing; private bool _isLongPressTriggered; private Coroutine _longPressCoroutine; private Vector3 _originalScale; private Vector3 _originalPosition; // 组件引用 private BlockShape _blockShape; private EventTriggerEx _eventTrigger; public bool IsDraggable { get => isDraggable; set => isDraggable = value; } public IBlockPuzzleGrid TargetGrid => BlockPuzzleSystem.Instance.grid; public IBlockShape BlockShape => _blockShape; // 事件 public event Action OnDragStart; public event Action OnDragUpdate; public event Action OnDragEnd; private void Awake() { _blockShape = GetComponent(); _eventTrigger = GetComponent(); RegisterEvent(); } private void OnDestroy() { _eventTrigger.UnRegister(EventTriggerType.PointerDown, OnPointerDown); _eventTrigger.UnRegister(EventTriggerType.PointerUp, OnPointerUp); _eventTrigger.UnRegister(EventTriggerType.PointerClick, OnPointerClick); // 清理协程 if (_longPressCoroutine != null) { StopCoroutine(_longPressCoroutine); _longPressCoroutine = null; } } private void RegisterEvent() { _eventTrigger.Register(EventTriggerType.PointerDown, OnPointerDown); _eventTrigger.Register(EventTriggerType.PointerUp, OnPointerUp); _eventTrigger.Register(EventTriggerType.PointerClick, OnPointerClick); } private void Update() { if (CurrentState == DragState.Dragging) { UpdateDrag(); } } private void OnPointerDown(BaseEventData eventData) { if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) { return; } if (!isDraggable) return; // 只有在静止状态才能开始长按 if (CurrentState == DragState.Idle || CurrentState == DragState.Placed) { _isPressing = true; _isLongPressTriggered = false; _longPressCoroutine = StartCoroutine(LongPressCoroutine()); } } private void OnPointerUp(BaseEventData eventData) { if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) { return; } // 如果还在长按过程中(未完成),则取消 if (_isPressing && !_isLongPressTriggered) { CancelLongPress(); } } private void OnPointerClick(BaseEventData eventData) { if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) { return; } // 只有在拖拽中点击才结束拖拽 if (CurrentState == DragState.Dragging && _isLongPressTriggered) { EndDrag(); } } public void StartDrag() { if (!isDraggable) return; // 如果原本就在网格中,则先移除 TargetGrid?.TryRemoveShapeFromGrid(_blockShape); TargetGrid?.ResetPreviewGrid(); CurrentState = DragState.Dragging; OnDragStart?.Invoke(_blockShape); } public void EndDrag() { // 清理长按状态 CancelLongPress(); // 先吸附到回收槽 var recycled = BlockPuzzleSystem.Instance.TrySnapToRecycleSlot(_blockShape); if (recycled) { CurrentState = DragState.Idle; _snapResult = SnapResult.RecycleResult; TargetGrid.ShowSnapPreview(_snapResult); OnDragEnd?.Invoke(_blockShape, CurrentState == DragState.Placed); return; } // 再吸附到网格 TargetGrid?.ResetPreviewGrid(); if (_snapResult.canSnap) { CurrentState = DragState.Placed; transform.localScale = Vector3.one; TargetGrid.TrySnapToGrid(_blockShape, _snapResult.RootCell); // 处理Cursor _eventTrigger.OnPointerUp(null); OnDragEnd?.Invoke(_blockShape, CurrentState == DragState.Placed); } else { // 都没吸附到,恢复原状态和缩放 transform.localScale = Vector3.one; } TargetGrid.GridVisualizer.UpdateAllCellsVisual(); } public void UpdateDrag() { // 检测右键点击进行旋转(仅在拖动中) if (Input.GetMouseButtonDown(1)) // 1 = 右键 { _blockShape.RotateClockwise(); } // 更新拖拽位置 transform.position = GetMouseWorldPosition() - _blockShape.GetCenterPosition(); // 进行Snap检查 _snapResult = TargetGrid.CheckSnapToGrid(_blockShape); // 在网格显示预览 TargetGrid.ShowSnapPreview(_snapResult); OnDragUpdate?.Invoke(_blockShape); } private void HandleStateChange(DragState newState) { var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup == null) return; switch (newState) { case DragState.Idle: case DragState.Dragging: sortingGroup.sortingOrder = dragLayerOrder; sortingGroup.sortingLayerID = dragLayerId; break; case DragState.Placed: sortingGroup.sortingOrder = idleLayerOrder; sortingGroup.sortingLayerID = idleLayerId; break; } } private Vector3 GetMouseWorldPosition() { var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); return new Vector3(mouseWorldPos.x, mouseWorldPos.y, 0); } private IEnumerator LongPressCoroutine() { float elapsed = 0f; _originalScale = transform.localScale; _originalPosition = transform.localPosition; while (elapsed < longPressDuration) { elapsed += Time.deltaTime; float progress = elapsed / longPressDuration; // 抖动效果(轻微) float shake = Mathf.Sin(elapsed * 30f) * shakeIntensity * progress; transform.localPosition = _originalPosition + Vector3.right * shake; // 放大效果(缓慢 EaseOut) float scale = Mathf.Lerp(1f, longPressScale, EaseOutQuad(progress)); transform.localScale = _originalScale * scale; yield return null; } // 完成长按,开始拖拽 _isLongPressTriggered = true; StartDrag(); } private float EaseOutQuad(float t) { return t * (2f - t); } private void CancelLongPress() { if (_longPressCoroutine != null) { StopCoroutine(_longPressCoroutine); _longPressCoroutine = null; } // 恢复原始状态 if (_isPressing && !_isLongPressTriggered) { transform.localScale = _originalScale; transform.localPosition = _originalPosition; } _isPressing = false; _isLongPressTriggered = false; } // 实现IInteraction接口 public bool IsActive => true; public bool IsAvailable => true; public GameObject GetGameObject() => gameObject; } }