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;
}
}