2025-05-13 15:12:46 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class DirectDragHandler : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
[Tooltip("是否在拖拽时限制移动范围")]
|
|
|
|
|
|
public bool restrictMovement = false;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("拖拽范围限制(如果启用限制)")]
|
|
|
|
|
|
public RectTransform movementBounds;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("是否锁定垂直方向移动")]
|
|
|
|
|
|
public bool lockVertical = false;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("是否锁定水平方向移动")]
|
|
|
|
|
|
public bool lockHorizontal = false;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("拖拽时是否改变视觉效果")]
|
|
|
|
|
|
public bool changeDragAppearance = true;
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("拖拽时的缩放")]
|
|
|
|
|
|
public Vector3 dragScale = new Vector3(1.1f, 1.1f, 1.1f);
|
|
|
|
|
|
|
|
|
|
|
|
// 原始位置和缩放
|
|
|
|
|
|
private Vector3 originalPosition;
|
|
|
|
|
|
private Vector3 originalScale;
|
|
|
|
|
|
private RectTransform rectTransform;
|
|
|
|
|
|
|
2025-05-13 19:51:22 +08:00
|
|
|
|
private float scaleFactor;
|
|
|
|
|
|
|
2025-05-13 15:12:46 +08:00
|
|
|
|
// 拖拽状态
|
|
|
|
|
|
private bool isDragging = false;
|
2025-06-12 14:42:46 +08:00
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
2025-05-13 15:12:46 +08:00
|
|
|
|
private ButtonUpDrag buttonUpDrag;
|
|
|
|
|
|
private ScrollRect parentScrollRect;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检测拖拽位置是否可以放置
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Func<Vector2, bool> checkValidFunction;
|
|
|
|
|
|
|
|
|
|
|
|
private Action onStartDrag;
|
|
|
|
|
|
private Action onEndDrag;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 接收原始位置并返回调整后位置的函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Func<Vector2, Vector2> positionAdjuster;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理成功放置逻辑的函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<Vector2> placementHandler;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理拖拽过程中检测的函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Action<Vector2> dragUpdateHandler;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 是否启用拖拽功能
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool isDraggableEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform = GetComponent<RectTransform>();
|
|
|
|
|
|
|
|
|
|
|
|
if (buttonUpDrag == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("DirectDragHandler需要ButtonUpDrag组件!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 注册长按事件
|
|
|
|
|
|
buttonUpDrag.AddLongPress(OnStartDrag);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Init(Func<Vector2, bool> checkValidFunction, Action onStartDrag, Action onEndDrag)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.checkValidFunction = checkValidFunction;
|
|
|
|
|
|
this.onStartDrag = onStartDrag;
|
|
|
|
|
|
this.onEndDrag = onEndDrag;
|
2025-05-30 14:37:04 +08:00
|
|
|
|
|
|
|
|
|
|
// 查找父级ScrollRect
|
|
|
|
|
|
parentScrollRect = GetComponentInParent<ScrollRect>();
|
2025-05-13 15:12:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置位置调整处理器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="positionAdjuster">接收原始位置并返回调整后位置的函数</param>
|
|
|
|
|
|
public void SetPositionAdjuster(Func<Vector2, Vector2> positionAdjuster)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.positionAdjuster = positionAdjuster;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置成功放置处理器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="placementHandler">处理成功放置逻辑的函数</param>
|
|
|
|
|
|
public void SetPlacementHandler(Action<Vector2> placementHandler)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.placementHandler = placementHandler;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置拖拽过程中的位置检测处理器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dragUpdateHandler">在拖拽过程中处理位置检测的函数</param>
|
|
|
|
|
|
public void SetDragUpdateHandler(Action<Vector2> dragUpdateHandler)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.dragUpdateHandler = dragUpdateHandler;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 启用或禁用拖拽功能
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetDraggable(bool draggable)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.isDraggableEnabled = draggable;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnStartDrag()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isDraggableEnabled)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// 开始拖拽
|
|
|
|
|
|
isDragging = true;
|
2025-05-13 19:51:22 +08:00
|
|
|
|
scaleFactor = UIRoot.Instance.RootCanvas.GetComponent<RectTransform>().sizeDelta.x / Screen.width;
|
2025-05-13 15:12:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果在ScrollRect中,则暂时禁用它
|
|
|
|
|
|
if (parentScrollRect != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
parentScrollRect.enabled = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
originalPosition = rectTransform.position;
|
|
|
|
|
|
originalScale = rectTransform.localScale;
|
|
|
|
|
|
|
|
|
|
|
|
// 改变外观(如果启用)
|
|
|
|
|
|
if (changeDragAppearance)
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.localScale = dragScale;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 注册移动和结束拖拽事件
|
|
|
|
|
|
InputManager.Instance.OnFingerMoveUI += UpdateDragPosition;
|
|
|
|
|
|
InputManager.Instance.OnFingerUpUI += EndDrag;
|
|
|
|
|
|
|
|
|
|
|
|
onStartDrag?.Invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void UpdateDragPosition(Vector2 delta)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isDragging || !isDraggableEnabled)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// 计算新位置(考虑偏移)
|
2025-05-13 19:51:22 +08:00
|
|
|
|
Vector2 newPosition = rectTransform.anchoredPosition + delta * scaleFactor;
|
2025-05-13 15:12:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 应用方向锁定和边界限制逻辑...
|
|
|
|
|
|
|
|
|
|
|
|
// 使用自定义位置调整器(如果有)
|
|
|
|
|
|
if (positionAdjuster != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
newPosition = positionAdjuster(newPosition);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新位置
|
|
|
|
|
|
rectTransform.anchoredPosition = newPosition;
|
|
|
|
|
|
|
|
|
|
|
|
// 调用拖拽更新处理器
|
|
|
|
|
|
dragUpdateHandler?.Invoke(rectTransform.position);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void EndDrag(Vector2 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isDragging)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
isDragging = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复原始外观
|
|
|
|
|
|
if (changeDragAppearance)
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.localScale = originalScale;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拖拽结束逻辑
|
|
|
|
|
|
HandleDragEnd(rectTransform.position);//position
|
|
|
|
|
|
|
|
|
|
|
|
// 重新启用ScrollRect
|
|
|
|
|
|
if (parentScrollRect != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
parentScrollRect.enabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理事件监听
|
|
|
|
|
|
InputManager.Instance.OnFingerMoveUI -= UpdateDragPosition;
|
|
|
|
|
|
InputManager.Instance.OnFingerUpUI -= EndDrag;
|
|
|
|
|
|
|
|
|
|
|
|
onEndDrag?.Invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void HandleDragEnd(Vector2 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 在这里实现放置逻辑
|
|
|
|
|
|
bool validPlacement = CheckValidPlacement(position);
|
|
|
|
|
|
|
|
|
|
|
|
if (validPlacement)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 有效放置,执行相应逻辑
|
|
|
|
|
|
FinalizeItemPlacement(position);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool CheckValidPlacement(Vector2 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 实现放置检测逻辑
|
|
|
|
|
|
// 这里只是示例,你需要根据具体需求实现
|
|
|
|
|
|
|
|
|
|
|
|
// 示例:使用射线检测
|
|
|
|
|
|
//var results = new System.Collections.Generic.List<UnityEngine.EventSystems.RaycastResult>();
|
|
|
|
|
|
//var eventData = new UnityEngine.EventSystems.PointerEventData(UnityEngine.EventSystems.EventSystem.current);
|
|
|
|
|
|
//eventData.position = position;
|
|
|
|
|
|
//UnityEngine.EventSystems.EventSystem.current.RaycastAll(eventData, results);
|
|
|
|
|
|
//
|
|
|
|
|
|
//foreach (var result in results)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// // 检查是否是有效的放置目标
|
|
|
|
|
|
// // 例如:检查是否有特定的组件或标签
|
|
|
|
|
|
// if (result.gameObject.CompareTag("DropZone"))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// return true;
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
// 默认返回true,允许在任何位置放置
|
|
|
|
|
|
// 如果需要更严格的控制,改为return false;
|
|
|
|
|
|
//return true;
|
|
|
|
|
|
|
|
|
|
|
|
if (checkValidFunction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return checkValidFunction(position);
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void FinalizeItemPlacement(Vector2 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (placementHandler != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
placementHandler(position);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ReturnToOriginalPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 返回原始位置
|
|
|
|
|
|
// 可以使用动画或直接设置
|
|
|
|
|
|
|
|
|
|
|
|
// 直接设置:
|
|
|
|
|
|
// rectTransform.position = originalPosition;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用动画效果:
|
|
|
|
|
|
//StartCoroutine(AnimateReturn());
|
|
|
|
|
|
|
|
|
|
|
|
rectTransform.position = originalPosition;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private System.Collections.IEnumerator AnimateReturn()
|
|
|
|
|
|
{
|
|
|
|
|
|
float duration = 0.3f;
|
|
|
|
|
|
float elapsed = 0f;
|
|
|
|
|
|
Vector3 startPos = rectTransform.position;
|
|
|
|
|
|
|
|
|
|
|
|
while (elapsed < duration)
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.position = Vector3.Lerp(startPos, originalPosition, elapsed / duration);
|
|
|
|
|
|
elapsed += Time.deltaTime;
|
|
|
|
|
|
yield return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rectTransform.position = originalPosition;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 清理事件监听
|
|
|
|
|
|
if (InputManager.Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
InputManager.Instance.OnFingerMoveUI -= UpdateDragPosition;
|
|
|
|
|
|
InputManager.Instance.OnFingerUp -= EndDrag;
|
|
|
|
|
|
InputManager.Instance.OnFingerUpUI -= EndDrag;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 可选:提供公共方法以在外部重置位置
|
|
|
|
|
|
public void ResetPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.position = originalPosition;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 可选:提供公共方法以更新原始位置(例如在布局变化后)
|
|
|
|
|
|
public void UpdateOriginalPosition()
|
|
|
|
|
|
{
|
|
|
|
|
|
originalPosition = rectTransform.position;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|