using System; using AibisDream.Framework; using DG.Tweening; using UnityEngine; using UnityEngine.EventSystems; namespace AibisDream { public class ShapeDragger : MonoBehaviour, IInteraction { #region 编辑器字段 [SerializeField] private bool isInteractable = true; [Header("交互模式(由外界赋值控制)")] [SerializeField] private InteractionMode interactionMode = InteractionMode.Drag; [SerializeField] private int sortingLayerId; [SerializeField] private int dragLayerOrder; [SerializeField] private int pressLayerOrder; /// /// 闲置时层级,由 BlockShape 在 LoadVisualPrefab 后从 Visual 的 SortingGroup 赋值 /// public int IdleLayerOrder { get; set; } [Header("长按设置")] [SerializeField] private float longPressDuration = 0.65f; [SerializeField] private float longPressScale = 1.1f; [SerializeField] private float shakeIntensity = 1f; [Header("拖拽跟随")] [SerializeField] private float dragFollowSpeed = 15f; #endregion // 拖拽状态 private DragState _currentState = DragState.Placed; public DragState CurrentState { get => _currentState; set { if (value == _currentState) return; // 处理状态切换 HandleStateChange(value); _currentState = value; } } // 长按相关状态 private bool _isPressing; private bool _isLongPressTriggered; private bool _canEndDragOnClick; // 标记是否可以通过点击结束拖拽 private Sequence _longPressSequence; private Vector3 _originalScale; private Vector3 _originalPosition; // 组件引用 private BlockShape _blockShape; private EventTriggerEx _eventTrigger; public bool IsInteractable { get => isInteractable; set => isInteractable = value; } public InteractionMode InteractionMode { get => interactionMode; set => interactionMode = value; } public IBlockShape BlockShape => _blockShape; #region 事件定义 /// /// 拖拽开始事件 - 携带详细信息 /// public event EventHandler OnDragStartDetailed; /// /// 拖拽更新事件 - 携带详细信息 /// public event EventHandler OnDragUpdateDetailed; /// /// 拖拽结束事件 - 携带详细信息,由订阅者处理具体逻辑 /// public event EventHandler OnDragEndDetailed; /// /// 点击事件 /// public event Action OnClick; // 保留原有简单事件以保持向后兼容 public event Action OnDragStart; public event Action OnDragUpdate; public event Action OnDragEnd; #endregion 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); // 清理 DOTween 动画 _longPressSequence?.Kill(); transform.DOKill(); } 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 (!isInteractable) return; // 如果当前正在拖拽中,标记可以通过点击结束 if (CurrentState == DragState.Dragging) { _canEndDragOnClick = true; } // 只有在静止状态才能开始拖拽(点击模式不启动拖拽) else if (interactionMode == InteractionMode.Drag) { if (CurrentState == DragState.Placed) { // Placed状态:需要长按才能拖拽(防止误触) _isPressing = true; _isLongPressTriggered = false; _canEndDragOnClick = false; // 长按开始时不允许点击结束 StartLongPressAnimation(); } else if (CurrentState == DragState.Idle) { // Idle状态:点击立即开始拖拽(快速取用) StartDrag(); } } } 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 (interactionMode == InteractionMode.Click) { if (!isInteractable) return; OnClick?.Invoke(_blockShape); PlayClickFeedback(); return; } // 拖拽模式:只有在拖拽中且允许点击结束时才结束拖拽 if (CurrentState == DragState.Dragging && _canEndDragOnClick) { EndDrag(); _canEndDragOnClick = false; // 重置标记 } } public void StartDrag() { if (!isInteractable) return; // 记录原始位置 _originalPosition = transform.position; CurrentState = DragState.Dragging; // 创建事件参数 var args = new DragStartEventArgs { Shape = _blockShape, StartPosition = transform.position }; // 触发详细事件 OnDragStartDetailed?.Invoke(this, args); // 触发兼容事件 OnDragStart?.Invoke(_blockShape); } public void EndDrag() { // 清理长按状态 CancelLongPress(); // 创建事件参数,由订阅者处理具体逻辑 var args = new DragEndEventArgs { Shape = _blockShape, EndPosition = transform.position, FinalState = DragState.Idle, // 默认状态 IsHandled = false, ResultType = DragEndResultType.None }; // 触发事件,让订阅者处理回收、吸附等逻辑 OnDragEndDetailed?.Invoke(this, args); // 根据事件处理结果更新状态 if (args.IsHandled) { CurrentState = args.FinalState; // 应用视觉状态 ApplyVisualState(args.FinalState); } else { // 未被处理,恢复原状 CurrentState = DragState.Idle; transform.localScale = Vector3.one; // 可选:播放未吸附的动画(如弹回) PlayDropAnimation(); } // 触发兼容事件 OnDragEnd?.Invoke(_blockShape, CurrentState == DragState.Placed); } /// /// 应用视觉状态 /// private void ApplyVisualState(DragState state) { switch (state) { case DragState.Placed: transform.localScale = Vector3.one; break; case DragState.Idle: transform.localScale = Vector3.one; break; } } /// /// 播放放下动画(未吸附时) /// private void PlayDropAnimation() { // 可以在这里实现弹回动画或其他视觉反馈 transform.localScale = Vector3.one; } public void UpdateDrag() { // 检测右键点击进行旋转(仅在拖动中) if (Input.GetMouseButtonDown(1)) // 1 = 右键 { _blockShape.RotateClockwise(); // 旋转后立即重置位置,避免 Lerp 造成的延迟感 var snappedPos = GetMouseWorldPosition() - _blockShape.GetCenterPosition(); transform.position = snappedPos; } // 更新拖拽位置(带轻微滞后感) var targetPos = GetMouseWorldPosition() - _blockShape.GetCenterPosition(); transform.position = Vector3.Lerp(transform.position, targetPos, Time.deltaTime * dragFollowSpeed); // 创建事件参数 var args = new DragUpdateEventArgs { Shape = _blockShape, CurrentPosition = transform.position }; // 触发详细事件 - 由订阅者处理 Snap 检查和预览显示 OnDragUpdateDetailed?.Invoke(this, args); // 触发兼容事件 OnDragUpdate?.Invoke(_blockShape); } private void HandleStateChange(DragState newState) { var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup == null) return; sortingGroup.sortingLayerID = sortingLayerId; switch (newState) { case DragState.Idle: case DragState.Dragging: sortingGroup.sortingOrder = dragLayerOrder; break; case DragState.Placed: sortingGroup.sortingOrder = IdleLayerOrder; break; } } private Vector3 GetMouseWorldPosition() { var mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); return new Vector3(mouseWorldPos.x, mouseWorldPos.y, 0); } private void StartLongPressAnimation() { _originalScale = transform.localScale; _originalPosition = transform.localPosition; // 开始抖动时提升层级 var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup != null) { sortingGroup.sortingLayerID = sortingLayerId; sortingGroup.sortingOrder = dragLayerOrder; } // 杀掉之前的动画 _longPressSequence?.Kill(); // 创建 DOTween 序列 _longPressSequence = DOTween.Sequence(); // 放大效果(使用 EaseOutQuad) _longPressSequence.Append( transform.DOScale(_originalScale * longPressScale, longPressDuration) .SetEase(Ease.OutQuad) ); // 抖动效果(使用 DOShakePosition,与放大同时进行) _longPressSequence.Join( transform.DOShakePosition( longPressDuration-0.35f, new Vector3(shakeIntensity, shakeIntensity, 0), // 只在 X 轴抖动 60, // 振动频率 90, // 随机性 false, // 不淡出 true // 使用局部坐标 ) .SetEase(Ease.OutCubic) ); // 完成时的回调 _longPressSequence.OnComplete(() => { _isLongPressTriggered = true; StartDrag(); }); // 设置为不自动销毁 _longPressSequence.SetAutoKill(true); } private void CancelLongPress() { // 杀掉 DOTween 动画 if (_longPressSequence != null && _longPressSequence.IsActive()) { _longPressSequence.Kill(); _longPressSequence = null; } // 恢复原始状态 if (_isPressing && !_isLongPressTriggered) { transform.DOKill(); // 确保所有动画都停止 transform.localScale = _originalScale; transform.localPosition = _originalPosition; // 抖动但没拿起来,降回原层级 var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup != null) { sortingGroup.sortingOrder = IdleLayerOrder; } } _isPressing = false; _isLongPressTriggered = false; _canEndDragOnClick = false; } /// /// 恢复点击反馈状态(缩放与层级),供外部在切换选中或切换模式时调用 /// public void ResetClickFeedback() { transform.DOKill(); transform.localScale = Vector3.one; var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup != null) { sortingGroup.sortingOrder = IdleLayerOrder; } } private void PlayClickFeedback() { transform.DOScale(1.05f, 0.15f).SetEase(Ease.OutQuad); var sortingGroup = _blockShape?.SortingGroup; if (sortingGroup != null) { sortingGroup.sortingOrder = pressLayerOrder; } } // 实现IInteraction接口 public bool IsActive => true; public bool IsAvailable => true; public GameObject GetGameObject() => gameObject; } }