Files
aibis-dream/Assets/Scripts/MiniGame/BlockPuzzle/Class/ShapeDragger.cs
T

449 lines
15 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, 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;
/// <summary>
/// 闲置时层级,由 BlockShape 在 LoadVisualPrefab 后从 Visual 的 SortingGroup 赋值
/// </summary>
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 IBlockPuzzleGrid TargetGrid => BlockPuzzleSystem.Instance.grid;
public IBlockShape BlockShape => _blockShape;
#region 事件定义
/// <summary>
/// 拖拽开始事件 - 携带详细信息
/// </summary>
public event EventHandler<DragStartEventArgs> OnDragStartDetailed;
/// <summary>
/// 拖拽更新事件 - 携带详细信息
/// </summary>
public event EventHandler<DragUpdateEventArgs> OnDragUpdateDetailed;
/// <summary>
/// 拖拽结束事件 - 携带详细信息,由订阅者处理具体逻辑
/// </summary>
public event EventHandler<DragEndEventArgs> OnDragEndDetailed;
/// <summary>
/// 点击事件
/// </summary>
public event Action<BlockShape> OnClick;
// 保留原有简单事件以保持向后兼容
public event Action<BlockShape> OnDragStart;
public event Action<BlockShape> OnDragUpdate;
public event Action<BlockShape, bool> OnDragEnd;
#endregion
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();
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);
}
/// <summary>
/// 应用视觉状态
/// </summary>
private void ApplyVisualState(DragState state)
{
switch (state)
{
case DragState.Placed:
transform.localScale = Vector3.one;
break;
case DragState.Idle:
transform.localScale = Vector3.one;
break;
}
}
/// <summary>
/// 播放放下动画(未吸附时)
/// </summary>
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;
}
/// <summary>
/// 恢复点击反馈状态(缩放与层级),供外部在切换选中或切换模式时调用
/// </summary>
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;
}
}