Search Unity

Unity UI Share a Drag UI and keep in screen script which is compatible with any canvas render mode.

Discussion in 'UGUI & TextMesh Pro' started by kei233, Sep 7, 2022.

  1. kei233

    kei233

    Joined:
    Dec 16, 2019
    Posts:
    59
    and want to know is there any situation has not considered.

    Edit: bug on different resolutions , fixed on #2

    Code (CSharp):
    1. /*
    2. * add this script on the UI you want to drag
    3. *this component should have a <Canvas> in any parents
    4. * <Canvas> render mods does not matter
    5. * <Canvas>should have a <Canvas Scaler> with  setting:
    6. * UI scale mode: scale with screen size
    7. * screen match mode : expand
    8. * other settings is not tested
    9. */
    10.  
    11. using UnityEngine;
    12. using UnityEngine.EventSystems;
    13.  
    14.  
    15.  
    16.  
    17. public class DragUI : MonoBehaviour, IDragHandler
    18. {
    19.     private RectTransform rt;
    20.  
    21.     private Vector3 pos;
    22.  
    23.     public Camera eventCamera;
    24.     private Rect canvasRect;
    25.     private Rect screenRect;
    26.     private Vector2 thisUISize;
    27.  
    28.  
    29.  
    30.  
    31.     private void Start()
    32.     {
    33.         rt = GetComponent<RectTransform>();
    34.         eventCamera = GetComponentInParent<Canvas>().worldCamera;
    35.         canvasRect = GetComponentInParent<Canvas>().GetComponent<RectTransform>().rect;
    36.     }
    37.  
    38.     private void OnValidate()
    39.     {
    40.         if (!GetComponent<RectTransform>())
    41.             Debug.LogWarning("DragUI needs <RectTransform> on itself");
    42.         if (!GetComponentInParent<Canvas>())
    43.             Debug.LogWarning("DragUI needs a <Canvas> in any parents");
    44.     }
    45.  
    46.     public void OnDrag(PointerEventData eventData)
    47.     {
    48.         SetPosition_KeepInScreen(eventData);
    49.     }
    50.  
    51.     private void SetPosition_KeepInScreen(PointerEventData eventData)
    52.     {
    53.         if (GetComponentInParent<Canvas>().renderMode is RenderMode.ScreenSpaceOverlay)
    54.         {
    55.             pos = eventData.position;
    56.             screenRect = new Rect(0, 0, Screen.width, Screen.height); //screen size
    57.             thisUISize = rt.sizeDelta * (Screen.width / canvasRect.width); //fix scaled size
    58.         }
    59.         else
    60.         {
    61.             var screenPoint = Input.mousePosition;
    62.             //distance of the plane from the camera
    63.             screenPoint.z = Mathf.Abs(eventCamera.transform.position.z - rt.position.z);
    64.             pos = eventCamera!.ScreenToWorldPoint(screenPoint);
    65.  
    66.             screenRect = canvasRect; //canvas size for
    67.             thisUISize = new Vector2(rt.rect.width, rt.rect.height);
    68.         }
    69.  
    70.         var leftBorder = thisUISize.x * rt.pivot.x;
    71.         var rightBorder = screenRect.width - thisUISize.x * (1 - rt.pivot.x);
    72.         var topBorder = screenRect.height - thisUISize.y * (1 - rt.pivot.y);
    73.         var bottomBorder = thisUISize.y * (rt.pivot.y);
    74.         pos.x = Mathf.Clamp(pos.x, leftBorder, rightBorder); //limit x
    75.         pos.y = Mathf.Clamp(pos.y, bottomBorder, topBorder); //limit y
    76.  
    77.         //final position
    78.         transform.position = pos;
    79.     }
    80. }
    81.  
     
    Last edited: Sep 10, 2022
  2. kei233

    kei233

    Joined:
    Dec 16, 2019
    Posts:
    59
    version 2.0 fix bug on different resolutions
    don't know why shoud do it this way.
    I tried to move the ui to the each edges, recorded the positions, found out this way worked
    Code (CSharp):
    1. /*
    2. * this component should have a <Canvas> in any parents
    3. * <Canvas> render mods does not matter
    4. * <Canvas>should have a <Canvas Scaler> with  setting:
    5. * UI scale mode: scale with screen size
    6. * screen match mode : expand
    7. * other settings is not tested
    8. */
    9.  
    10. using System;
    11. using System.Linq;
    12. using UnityEngine;
    13. using UnityEngine.EventSystems;
    14. using UnityEngine.UI;
    15.  
    16.  
    17.  
    18.  
    19. public class DragUI : MonoBehaviour, IDragHandler
    20. {
    21.     private RectTransform rt;
    22.  
    23.     private Vector3 pos;
    24.  
    25.     public Camera eventCamera;
    26.  
    27.     //private Rect canvasRect => GetComponentInParent<Canvas>().GetComponent<RectTransform>().rect;
    28.     //private Rect screenRect;
    29.     private Vector2 thisUISize;
    30.     private Canvas canvas;
    31.     private CanvasScaler canvasScaler;
    32.  
    33.  
    34.  
    35.  
    36.     private void Start()
    37.     {
    38.         rt = GetComponent<RectTransform>();
    39.         eventCamera = GetComponentInParent<Canvas>().worldCamera;
    40.         canvas = GetComponentsInParent<Canvas>().Last();
    41.         canvasScaler = canvas.GetComponent<CanvasScaler>();
    42.         if (canvasScaler && canvasScaler.screenMatchMode is not CanvasScaler.ScreenMatchMode.Expand)
    43.             Debug.LogWarning("matchMode of canvasScaler of canvas of DragUI must be expand", canvasScaler);
    44.     }
    45.  
    46.     private void OnValidate()
    47.     {
    48.         if (gameObject.activeInHierarchy)
    49.         {
    50.             if (!GetComponent<RectTransform>())
    51.                 Debug.LogWarning("DragUI needs <RectTransform> on itself", gameObject);
    52.  
    53.             canvas = GetComponentsInParent<Canvas>().LastOrDefault();
    54.             if (!canvas)
    55.             {
    56.                 Debug.LogWarning("DragUI needs a <Canvas> in any parents", gameObject);
    57.                 return;
    58.             }
    59.  
    60.             canvasScaler = canvas!.GetComponent<CanvasScaler>();
    61.             if (canvasScaler && canvasScaler.screenMatchMode is not CanvasScaler.ScreenMatchMode.Expand)
    62.                 Debug.LogWarning("canvasScaler matchMode must be expand", canvasScaler);
    63.         }
    64.     }
    65.  
    66.     public void OnDrag(PointerEventData eventData)
    67.     {
    68.         SetPosition_KeepInScreen(eventData);
    69.         //Tooltips.HideTooltips();//限制位置时 可能让鼠标指向 自身上某些物品
    70.     }
    71.  
    72.     private void SetPosition_KeepInScreen(PointerEventData eventData)
    73.     {
    74.         if (GetComponentInParent<Canvas>().renderMode is RenderMode.ScreenSpaceOverlay)
    75.         {
    76.             pos = eventData.position;
    77.         }
    78.         else
    79.         {
    80.             var screenPoint = Input.mousePosition;
    81.             //distance of the plane from the camera
    82.             screenPoint.z = GetComponentsInParent<Canvas>().Last().planeDistance;
    83.             pos = eventCamera!.ScreenToWorldPoint(screenPoint);
    84.         }
    85.  
    86.         thisUISize = new Vector2(rt.rect.width, rt.rect.height);
    87.  
    88.         //不同分辨率 还要调整
    89.         var yDelta = Screen.height / canvas.GetComponent<RectTransform>().rect.height;
    90.         var xDelta = Screen.width / canvas.GetComponent<RectTransform>().rect.width;
    91.         var scale = Math.Min(xDelta, yDelta);
    92.         print(scale);
    93.         //final position
    94.         transform.position = pos;
    95.         var left = Screen.width / 2f / scale;
    96.         var leftDelta = thisUISize.x * rt.pivot.x;
    97.         left = Math.Clamp(rt.anchoredPosition.x, -(left - leftDelta), left - thisUISize.x * (1 - rt.pivot.x));
    98.         var top = Screen.height / 2f / scale;
    99.         var topDelta = thisUISize.y * rt.pivot.y;
    100.         top = Math.Clamp(rt.anchoredPosition.y, -(top - topDelta), top - thisUISize.y * (1 - rt.pivot.y));
    101.         rt.anchoredPosition = new Vector2(left, top);
    102.     }
    103. }
    104.