Unity3D

[Unity] Drag and Drop | RectTransformUtility.ScreenPointToLocalPointInRectangle 사용 하기

DragonTory 2022. 12. 23. 23:53
반응형

Unity uGUI에서 image 나 button을 Drag and Drop  할 때 

IBeginDragHandler, IDragHandler, IEndDragHandler 의 세가지 인터페이스를 이용 하여 쉽게 구현 가능 하다.

 

OnBeginDrag:

마우스 버튼이 클릭 되면

우선 화면에 따라다는 오브젝트를 생성 하고

( dragItemPrefab : 실제 옮겨질 대상이 아니고 이 것을 이용 해서 대상을 표현 하고

원래 대상은 그냥 보이는 상태로 하던지 안 보이게 하던지 하면 된다. 

실제 대상을 이동 하게 하면 여러가지 생각 할 것이 많아 짐. )

OnDrag: 드래그 되고 있는 오브젝트를 마우스를 따라 다니게 하게 하면 된다.

이 때,

RectTransformUtility.ScreenPointToLocalPointInRectangle 을 사용 하여 

마우스 포지션으로 부터 UI 오브젝트의 localPosition으로 변환 하여 적용 하면 쉽게 이동 시킬 수 있다. 

Canvas.RenderMode 가 Screen Space - Overlay 일 경우 :

currentItem.transform.position = eventData.position;

로 해도 상관 없지만 

Canvas.RenderMode 가 Screen Space - Camera 인 경우 :

eventData.position을 현재 캔버스에 맞게 변환 해서 사용 해야 한다. 

Vector2 rectPosition = new();
RectTransformUtility.ScreenPointToLocalPointInRectangle(this.transform as RectTransform, mousePosition, canvas.worldCamera, out rectPosition);

 

참고)

Top Canvas 찾기 :

Unity Editor 말고 Script에서 UI가 적용 되고 있는 최상위 Canvas를 코드로 찾을 때 아래 메소드를 사용 한다.

transform.root.GetComponentInChildren<Canvas>();

var topCanvas = transform.root.GetComponentInChildren<Canvas>();

추가적으로 Canvas 프라퍼티 중에

canvas.isRootCanvas
canvas.rootCanvas

이런 것도 유용 할 수 있다. 

 

OnEndDrag: 마우스 버튼이 해제 되면 이때 따라다니던 대체 오브젝트를 없애거나 안 보이게 하고 

나머지 실제 원본 대상을 옮길지 말지 결정 하여 이동 처리 하면 된다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class DragAndDrop : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public Canvas canvas;
    public DragItem dragItemPrefab;

    DragItem currentItem;

    public void OnBeginDrag(PointerEventData eventData)
    {
        var enterObject = eventData.pointerEnter;
        var rayResults = eventData.pointerCurrentRaycast;

        CreateDragItem();

        currentItem.Show(true);

        MoveTo(currentItem.transform as RectTransform, eventData.position);

        print($"{this.transform.name} OnBeginDrag : {eventData.pointerDrag.transform.name}: {eventData.position}");
        print($"rayResults : {rayResults}");
    }

    public void OnDrag(PointerEventData eventData)
    {        
        MoveTo(currentItem.transform as RectTransform, eventData.position);

        print($"{this.transform.name} OnDrag : {eventData.pointerDrag.transform.name}: mouse {eventData.position}");

        var rayResults = eventData.pointerCurrentRaycast;
        print($"rayResults tag: {rayResults.gameObject.tag} : {rayResults.ToString()}");

    }
    public void OnEndDrag(PointerEventData eventData)
    {
        MoveTo(currentItem.transform as RectTransform, eventData.position);       
        DestroyDragItem();

        print($"{this.transform.name} OnEndDrag : {eventData.pointerDrag.transform.name}: {eventData.position}");
    }

    void MoveTo(RectTransform rectItem, Vector2 mousePosition)
    {
        if( rectItem == null)
        {
            print("Drag RectTransfrom is Null");
            return;
        }

        //RectTransformUtility.RectangleContainsScreenPoint()        
        Vector2 rectPosition = new();
        RectTransformUtility.ScreenPointToLocalPointInRectangle(this.transform as RectTransform, mousePosition, canvas.worldCamera, out rectPosition);

        //rectItem.anchoredPosition = rectPosition;
        rectItem.localPosition = rectPosition;

        print($"Rect Position {rectPosition}");       
    }

    void CreateDragItem()
    {
        DestroyDragItem();

        if (currentItem == null)
        {
            // rectTransform의 scale이 100이 안 되고 1이 되게 하려면 worldPositionStays = false여야 한다. 
            // Object Pool 로 변경 하자.
            currentItem = Instantiate(dragItemPrefab, this.transform, false) as DragItem;
        }
    }

    void DestroyDragItem()
    {
        if (currentItem != null)
        {
            // Object Pool 로 변경 하자.
            Destroy(currentItem.gameObject);
            currentItem = null;
        }
    }

}
반응형