using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; /// /// 可拖动的悬浮按钮 /// 点击打开调试工具,可以在屏幕上自由拖动 /// public class DraggableFloatingButton : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { [Header("设置")] [Tooltip("是否限制在屏幕范围内")] public bool clampToScreen = true; [Tooltip("与屏幕边缘的最小距离")] public float edgePadding = 20f; [Header("自动吸附")] [Tooltip("是否自动吸附到屏幕边缘")] public bool snapToEdge = true; [Tooltip("吸附动画时间")] public float snapDuration = 0.3f; private RectTransform rectTransform; private Canvas canvas; private Vector2 dragOffset; private bool isDragging = false; private Vector2 targetPosition; private bool isSnapping = false; // 用于区分点击和拖动 private Vector2 dragStartPosition; private const float clickThreshold = 5f; // 小于这个距离算点击 private bool wasDragging = false; // 标记刚才是否拖动过 private void Awake() { rectTransform = GetComponent(); canvas = GetComponentInParent(); if (canvas == null) { Debug.LogError("DraggableFloatingButton 必须在Canvas下!"); } targetPosition = rectTransform.anchoredPosition; } private void Update() { // 平滑移动到目标位置(吸附动画) if (isSnapping && !isDragging) { rectTransform.anchoredPosition = Vector2.Lerp( rectTransform.anchoredPosition, targetPosition, Time.deltaTime / snapDuration ); if (Vector2.Distance(rectTransform.anchoredPosition, targetPosition) < 0.5f) { rectTransform.anchoredPosition = targetPosition; isSnapping = false; } } } public void OnBeginDrag(PointerEventData eventData) { isDragging = true; isSnapping = false; // 记录起始位置,用于判断是点击还是拖动 dragStartPosition = eventData.position; // 计算拖动偏移 RectTransformUtility.ScreenPointToLocalPointInRectangle( canvas.transform as RectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPoint ); dragOffset = rectTransform.anchoredPosition - localPoint; } public void OnDrag(PointerEventData eventData) { if (!isDragging) return; // 转换屏幕坐标到Canvas局部坐标 RectTransformUtility.ScreenPointToLocalPointInRectangle( canvas.transform as RectTransform, eventData.position, eventData.pressEventCamera, out Vector2 localPoint ); // 应用拖动偏移 Vector2 newPosition = localPoint + dragOffset; // 限制在屏幕范围内 if (clampToScreen) { newPosition = ClampToScreen(newPosition); } rectTransform.anchoredPosition = newPosition; } public void OnEndDrag(PointerEventData eventData) { isDragging = false; // 计算拖动距离 float dragDistance = Vector2.Distance(dragStartPosition, eventData.position); // 如果拖动距离超过阈值,说明是拖动而不是点击 if (dragDistance > clickThreshold) { wasDragging = true; // 在下一帧重置标记 StartCoroutine(ResetDragFlag()); } // 自动吸附到最近的边缘 if (snapToEdge) { SnapToNearestEdge(); } } private System.Collections.IEnumerator ResetDragFlag() { yield return null; // 等待一帧 wasDragging = false; } /// /// 检查是否刚拖动过(用于Button点击事件判断) /// public bool GetWasDragging() { return wasDragging; } private Vector2 ClampToScreen(Vector2 position) { RectTransform canvasRect = canvas.transform as RectTransform; Vector2 canvasSize = canvasRect.sizeDelta; Vector2 buttonSize = rectTransform.sizeDelta; // 计算可移动范围 float minX = -canvasSize.x / 2 + buttonSize.x / 2 + edgePadding; float maxX = canvasSize.x / 2 - buttonSize.x / 2 - edgePadding; float minY = -canvasSize.y / 2 + buttonSize.y / 2 + edgePadding; float maxY = canvasSize.y / 2 - buttonSize.y / 2 - edgePadding; position.x = Mathf.Clamp(position.x, minX, maxX); position.y = Mathf.Clamp(position.y, minY, maxY); return position; } private void SnapToNearestEdge() { RectTransform canvasRect = canvas.transform as RectTransform; Vector2 canvasSize = canvasRect.sizeDelta; Vector2 currentPos = rectTransform.anchoredPosition; // 计算到各个边缘的距离 float distToLeft = Mathf.Abs(currentPos.x + canvasSize.x / 2); float distToRight = Mathf.Abs(currentPos.x - canvasSize.x / 2); float distToTop = Mathf.Abs(currentPos.y - canvasSize.y / 2); float distToBottom = Mathf.Abs(currentPos.y + canvasSize.y / 2); // 找到最近的边 float minDist = Mathf.Min(distToLeft, distToRight, distToTop, distToBottom); Vector2 snapPosition = currentPos; Vector2 buttonSize = rectTransform.sizeDelta; if (minDist == distToLeft) { // 吸附到左边 snapPosition.x = -canvasSize.x / 2 + buttonSize.x / 2 + edgePadding; } else if (minDist == distToRight) { // 吸附到右边 snapPosition.x = canvasSize.x / 2 - buttonSize.x / 2 - edgePadding; } else if (minDist == distToTop) { // 吸附到顶部 snapPosition.y = canvasSize.y / 2 - buttonSize.y / 2 - edgePadding; } else if (minDist == distToBottom) { // 吸附到底部 snapPosition.y = -canvasSize.y / 2 + buttonSize.y / 2 + edgePadding; } // 确保在屏幕范围内 snapPosition = ClampToScreen(snapPosition); targetPosition = snapPosition; isSnapping = true; } /// /// 设置悬浮按钮位置 /// public void SetPosition(Vector2 position) { if (clampToScreen) { position = ClampToScreen(position); } rectTransform.anchoredPosition = position; targetPosition = position; } /// /// 获取当前位置 /// public Vector2 GetPosition() { return rectTransform.anchoredPosition; } }