드래그 앤 드롭은 UI 디자인의 흔한 기능입니다. UI 툴킷을 사용하여 커스텀 에디터 창 또는 Unity에서 빌드한 애플리케이션 내부에 드래그 앤 드롭 UI를 만들 수 있습니다. 다음 예시는 커스텀 에디터 창 내에서 드래그 앤 드롭 UI를 만드는 방법을 보여줍니다.
예시에서는 커스텀 에디터 창에서 여러 슬롯과 하나의 오브젝트를 추가합니다. 아래 보이는 대로 오브젝트를 모든 슬롯으로 드래그할 수 있습니다.
이 예시에서 생성한 완성된 파일은 GitHub 저장소에서 확인할 수 있습니다.
이 고급 예시는 Unity 에디터, UI 툴킷, C# 스크립팅에 익숙한 개발자용입니다. 다음 개념에 대한 기본적인 이해가 있는 것이 좋습니다.
시작하려면 드래그 앤 드롭 UI를 담을 커스텀 에디터 창을 만듭니다.
Assets
에 DragAndDrop
이라는 폴더를 만들고 모든 파일을 저장합니다.DragAndDrop
폴더에서 마우스 오른쪽 버튼을 클릭하고 Create > UI Toolkit > Editor Window를 선택합니다.DragAndDropWindow
를 입력합니다.DragAndDropWindow.cs
를 열고 메뉴 이름과 창 제목을 Drag And Drop
으로 변경하고 기본 레이블에 대한 코드를 제거하여 UI를 더욱 사용자 친화적으로 만듭니다.완성된 DragAndDropWindow.cs
는 다음과 같이 작성되어야 합니다.
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
public class DragAndDropWindow : EditorWindow
{
[MenuItem("Window/UI Toolkit/Drag And Drop")]
public static void ShowExample()
{
DragAndDropWindow wnd = GetWindow<DragAndDropWindow>();
wnd.titleContent = new GUIContent("Drag And Drop");
}
public void CreateGUI()
{
// Each editor window contains a root VisualElement object
VisualElement root = rootVisualElement;
// Import UXML
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Drag and Drop/DragAndDropWindow.uxml");
VisualElement labelFromUXML = visualTree.Instantiate();
root.Add(labelFromUXML);
// A stylesheet can be added to a VisualElement.
// The style will be applied to the VisualElement and all of its children.
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Drag and Drop/DragAndDropWindow.uss");
}
}
다음으로 커스텀 창에 UI 컨트롤을 추가합니다.
DragAndDrop
폴더에서 DragAndDropWindow.uxml
을 더블 클릭하여 UI 빌더를 엽니다.
StyleSheet에서 Add Existing USS를 클릭하고 DragAndDropWindow.uss
를 선택합니다.
다음 VisualElement
UI 컨트롤을 추가합니다.
slots
이라는 슬롯 한 개에 slot_row1
과 slot_row2
라는 자식 슬롯이 있습니다. 각 행에는 slot1
과 slot2
라는 자식 슬롯이 각각 있습니다.slots
과 같은 수준에 object
라는 오브젝트가 한 개 있습니다. object
는 계층 구조에서 slots
다음에 와야 합니다.UI 컨트롤을 다음과 같이 스타일링합니다.
slot1
과 slot2
의 경우 흰색 배경에 둥근 모서리를 가진 80픽셀 X 80픽셀의 정사각형으로 스타일을 지정합니다. 각 행에 두 개의 슬롯이 있는 두 개의 행으로 슬롯을 정렬합니다.object
의 경우 검은색 배경에 50픽셀 X 50픽셀의 둥근 점으로 스타일을 지정합니다.
UI 컨트롤을 추가하고 스타일을 지정하는 방법에 대한 지침은 UI 빌더를 참조하십시오.
완성된 DragAndDropWindow.uxml
은 다음과 같이 작성되어야 합니다.
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<Style src="project://database/Assets/DragAndDrop/DragAndDropWindow.uss?fileID=7433441132597879392&guid=3d86870c8637c4a3c979a8b4fe0cba4c&type=3#DragAndDrop" />
<ui:VisualElement name="slots">
<ui:VisualElement name="slot_row1" class="slot_row">
<ui:VisualElement name="slot1" class="slot" />
<ui:VisualElement name="slot2" class="slot" />
</ui:VisualElement>
<ui:VisualElement name="slot_row2" class="slot_row">
<ui:VisualElement name="slot1" class="slot" />
<ui:VisualElement name="slot2" class="slot" />
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement name="object" class="object" />
</ui:UXML>
완성된 DragAndDropWindow.uss
는 다음과 같이 작성되어야 합니다.
.slot {
width: 80px;
height: 80px;
margin: 5px;
background-color: rgb(255, 255, 255);
border-top-radius: 10px;
}
.slot_row {
flex-direction: row;
}
.object {
width: 50px;
height: 50px;
position: absolute;
left: 10px;
top: 10px;
border-radius: 30px;
background-color: rgb(0, 0, 0);
}
드래그 앤 드롭 동작을 정의하려면 PointerManipulator
클래스를 확장하고 로직을 정의합니다.
DragAndDrop
폴더에서 DragAndDropManipulator.cs
라는 또 다른 C# 파일을 만듭니다.
DragAndDropManipulator.cs
파일을 엽니다.
using UnityEngine.UIElements;
선언을 추가합니다.
DragAndDropManipulator
클래스가 MonoBehaviour
가 아닌 PointerManipulator
를 확장하도록 만들고 다음을 수행합니다.
RegisterCallbacksOnTarget()
메서드를 구현하여 필요한 모든 콜백을 등록합니다.UnregisterCallbacksOnTarget()
메서드를 구현하여 해당 콜백의 등록을 취소합니다.target
을 설정하고 시각적 트리의 루트에 대한 레퍼런스를 저장하는 생성자를 작성합니다.PointerDownEvent
, PointerMoveEvent
, PointerUpEvent
, PointerCaptureOutEvent
에 대한 콜백 역할을 하는 네 가지 메서드를 작성합니다.
PointerDownHandler()
: target
의 시작 위치와 포인터를 저장하고 target
이 포인터를 캡처하도록 하며 드래그가 현재 진행 중임을 나타냅니다.PointerMoveHandler()
: 드래그가 진행 중인지 target
이 포인터를 캡처했는지 확인합니다. 두 가지 모두 true이면 창의 경계 내에서 target
에 대한 새로운 위치를 계산합니다.PointerUpHandler()
: 드래그가 진행 중인지 target
이 포인터를 캡처했는지 확인합니다. 두 가지 모두 true이면 target
이 포인터를 해제합니다.PointerCaptureOutHandler()
: 드래그가 진행 중인지 확인합니다. true인 경우 시각적 트리의 루트를 쿼리하여 모든 슬롯을 찾고 target
과 가장 가까운 슬롯을 결정하고 target
이 해당 슬롯의 맨 위에 놓이도록 위치를 설정합니다. 겹치는 슬롯이 없으면 target
의 위치를 원래 위치로 다시 설정합니다.RegisterCallbacksOnTarget()
에서 이 네 가지 콜백을 target
에 등록합니다.
UnregisterCallbacksOnTarget()
에서 이 네 가지 콜백을 target
에서 등록 취소합니다.
완성된 DragAndDropManipulator.cs
는 다음과 같이 작성되어야 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class DragAndDropManipulator : PointerManipulator
{
public DragAndDropManipulator(VisualElement target)
{
this.target = target;
root = target.parent;
}
protected override void RegisterCallbacksOnTarget()
{
target.RegisterCallback<PointerDownEvent>(PointerDownHandler);
target.RegisterCallback<PointerMoveEvent>(PointerMoveHandler);
target.RegisterCallback<PointerUpEvent>(PointerUpHandler);
target.RegisterCallback<PointerCaptureOutEvent>(PointerCaptureOutHandler);
}
protected override void UnregisterCallbacksFromTarget()
{
target.UnregisterCallback<PointerDownEvent>(PointerDownHandler);
target.UnregisterCallback<PointerMoveEvent>(PointerMoveHandler);
target.UnregisterCallback<PointerUpEvent>(PointerUpHandler);
target.UnregisterCallback<PointerCaptureOutEvent>(PointerCaptureOutHandler);
}
private Vector2 targetStartPosition { get; set; }
private Vector3 pointerStartPosition { get; set; }
private bool enabled { get; set; }
private VisualElement root { get; }
private void PointerDownHandler(PointerDownEvent evt)
{
targetStartPosition = target.transform.position;
pointerStartPosition = evt.position;
target.CapturePointer(evt.pointerId);
enabled = true;
}
private void PointerMoveHandler(PointerMoveEvent evt)
{
if (enabled && target.HasPointerCapture(evt.pointerId))
{
Vector3 pointerDelta = evt.position - pointerStartPosition;
target.transform.position = new Vector2(
Mathf.Clamp(targetStartPosition.x + pointerDelta.x, 0, target.panel.visualTree.worldBound.width),
Mathf.Clamp(targetStartPosition.y + pointerDelta.y, 0, target.panel.visualTree.worldBound.height));
}
}
private void PointerUpHandler(PointerUpEvent evt)
{
if (enabled && target.HasPointerCapture(evt.pointerId))
{
target.ReleasePointer(evt.pointerId);
}
}
private void PointerCaptureOutHandler(PointerCaptureOutEvent evt)
{
if (enabled)
{
VisualElement slotsContainer = root.Q<VisualElement>("slots");
UQueryBuilder<VisualElement> allSlots =
slotsContainer.Query<VisualElement>(className: "slot");
UQueryBuilder<VisualElement> overlappingSlots =
allSlots.Where(OverlapsTarget);
VisualElement closestOverlappingSlot =
FindClosestSlot(overlappingSlots);
Vector3 closestPos = Vector3.zero;
if (closestOverlappingSlot != null)
{
closestPos = RootSpaceOfSlot(closestOverlappingSlot);
closestPos = new Vector2(closestPos.x - 5, closestPos.y - 5);
}
target.transform.position =
closestOverlappingSlot != null ?
closestPos :
targetStartPosition;
enabled = false;
}
}
private bool OverlapsTarget(VisualElement slot)
{
return target.worldBound.Overlaps(slot.worldBound);
}
private VisualElement FindClosestSlot(UQueryBuilder<VisualElement> slots)
{
List<VisualElement> slotsList = slots.ToList();
float bestDistanceSq = float.MaxValue;
VisualElement closest = null;
foreach (VisualElement slot in slotsList)
{
Vector3 displacement =
RootSpaceOfSlot(slot) - target.transform.position;
float distanceSq = displacement.sqrMagnitude;
if (distanceSq < bestDistanceSq)
{
bestDistanceSq = distanceSq;
closest = slot;
}
}
return closest;
}
private Vector3 RootSpaceOfSlot(VisualElement slot)
{
Vector2 slotWorldSpace = slot.parent.LocalToWorld(slot.layout.position);
return root.WorldToLocal(slotWorldSpace);
}
}
커스텀 창에서 드래그 앤 드롭을 활성화하려면 창이 열릴 때 인스턴스화합니다.
DragAndDropWindow.cs
에서 CreateGUI()
메서드에 다음을 추가하여 DragAndDropManipulator
클래스를 인스턴스화합니다.
DragAndDropManipulator manipulator =
new(rootVisualElement.Q<VisualElement>("object"));
Unity 메뉴바로 이동하여 Window > UI Toolkit > Drag And Drop을 클릭합니다. 열린 커스텀 창에서 오브젝트를 아무 슬롯에나 드래그할 수 있습니다.
Unity 2021.2에서 드래그 앤 드롭 UI 예시 추가됨NewIn20212
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.