Files
aibis-dream/Assets/Scripts/MiniGame/BlockPuzzle/Class/ShapeDragger.cs
T
2026-02-11 21:04:26 +08:00

313 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using AibisDream.Framework;
using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;
namespace AibisDream
{
public class ShapeDragger : MonoBehaviour, IBlockShapeDragger, IInteraction
{
#region 编辑器字段
[SerializeField] private bool isDraggable = true;
[SerializeField] private int sortingLayerId;
[SerializeField] private int dragLayerOrder;
[SerializeField] private int idleLayerOrder;
[SerializeField] private int pressLayerOrder;
[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 SnapResult _snapResult;
// 长按相关状态
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 IsDraggable { get => isDraggable; set => isDraggable = value; }
public IBlockPuzzleGrid TargetGrid => BlockPuzzleSystem.Instance.grid;
public IBlockShape BlockShape => _blockShape;
// 事件
public event Action<IBlockShape> OnDragStart;
public event Action<IBlockShape> OnDragUpdate;
public event Action<IBlockShape, bool> OnDragEnd;
private void Awake()
{
_blockShape = GetComponent<BlockShape>();
_eventTrigger = GetComponent<EventTriggerEx>();
RegisterEvent();
}
private void OnDestroy()
{
_eventTrigger.UnRegister(EventTriggerType.PointerDown, OnPointerDown);
_eventTrigger.UnRegister(EventTriggerType.PointerUp, OnPointerUp);
_eventTrigger.UnRegister(EventTriggerType.PointerClick, OnPointerClick);
// 清理 DOTween 动画
_longPressSequence?.Kill();
}
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.Dragging)
{
_canEndDragOnClick = true;
}
// 只有在静止状态才能开始长按
else if (CurrentState == DragState.Idle || CurrentState == DragState.Placed)
{
_isPressing = true;
_isLongPressTriggered = false;
_canEndDragOnClick = false; // 长按开始时不允许点击结束
StartLongPressAnimation();
}
}
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 && _canEndDragOnClick)
{
EndDrag();
_canEndDragOnClick = false; // 重置标记
}
}
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();
}
// 更新拖拽位置(带轻微滞后感)
var targetPos = GetMouseWorldPosition() - _blockShape.GetCenterPosition();
transform.position = Vector3.Lerp(transform.position, targetPos, Time.deltaTime * dragFollowSpeed);
// 进行Snap检查
_snapResult = TargetGrid.CheckSnapToGrid(_blockShape);
// 在网格显示预览
TargetGrid.ShowSnapPreview(_snapResult);
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;
// 杀掉之前的动画
_longPressSequence?.Kill();
// 创建 DOTween 序列
_longPressSequence = DOTween.Sequence();
// 放大效果(使用 EaseOutQuad
_longPressSequence.Append(
transform.DOScale(_originalScale * longPressScale, longPressDuration)
.SetEase(Ease.OutQuad)
);
// 抖动效果(使用 DOShakePosition,与放大同时进行)
_longPressSequence.Join(
transform.DOShakePosition(
longPressDuration,
new Vector3(shakeIntensity, shakeIntensity, 0), // 只在 X 轴抖动
60, // 振动频率
90, // 随机性
false, // 不淡出
true // 使用局部坐标
)
);
// 完成时的回调
_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;
}
_isPressing = false;
_isLongPressTriggered = false;
_canEndDragOnClick = false;
}
// 实现IInteraction接口
public bool IsActive => true;
public bool IsAvailable => true;
public GameObject GetGameObject() => gameObject;
}
}