Buat menu tabbed untuk runtime
Buat daftar dan pemandangan pohon

Buat UI drag-and-drop di dalam jendela editor kustom

Drag-and-drop adalah fitur umum dalam desain UI(User Interface) Memungkinkan pengguna untuk berinteraksi dengan aplikasi Anda. Unity saat ini mendukung tiga sistem UI. More info
Lihat di Glossary
. Anda dapat menggunakan UI Toolkit untuk membuat UI drag-and-drop di dalam jendela editor kustom atau dalam aplikasi yang dibangun oleh Unity. Contoh ini menunjukkan cara membuat UI drag-and-drop di dalam jendela editor kustom.

Contoh gambaran

Contoh menambahkan beberapa slot dan satu objek di jendela editor kustom. Anda dapat menyeret objek ke dalam slot apa pun, seperti yang ditunjukkan di bawah ini:

A preview of a drag-and-drop UI
Pratinjau UI drag-and-drop

Anda dapat menemukan file yang lengkap yang contoh ini menciptakan di Repositori GitHub ini.

Prerequisites

Ini adalah contoh canggih untuk pengembang yang akrab dengan Editor Unity, UI Toolkit, dan C # scripting. Disarankan bahwa Anda memiliki pemahaman dasar tentang konsep berikut:

Buat jendela editor kustom

Untuk memulai, buat jendela editor kustom untuk menahan UI drag-and-drop Anda.

  1. Buat proyek di Unity menggunakan template.
  2. Buat folder di Assets yang disebut DragAndDrop untuk menyimpan semua file Anda.
  3. Dalam folder DragAndDrop, klik kanan dan pilih Create > UI Toolkit > Editor Window.
  4. Di UI Toolkit Editor Window Creator, masukkan DragAndDropWindow.
  5. Klik Confirm. Ini secara otomatis menciptakan file C#, UXML, dan USS untuk jendela kustom Anda.
  6. Buka DragAndDropWindow.cs dan ubah nama menu dan judul jendela ke Drag And Drop, dan lepaskan kode untuk label default, untuk membuat UI lebih ramah pengguna.

Anda selesai DragAndDropWindow.cs harus terlihat seperti berikut:

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");
    
    }
}

Buat slot dan objek

Selanjutnya, tambahkan kontrol UI ke jendela kustom Anda.

  1. Dalam folder DragAndDrop, klik dua DragAndDropWindow.uxml untuk membuka UI Builder.

  2. Pada StyleSheet, klik Add Existing USS dan pilih DragAndDropWindow.uss.

  3. Tambahkan kontrol UI VisualElement berikut:

    • Satu bernama slots dengan dua anak bernama slot_row1 dan slot_row2. Setiap baris harus memiliki dua anak bernama slot1 dan slot2.
    • Satu bernama object pada tingkat yang sama dengan slots. object harus datang setelah slots di Hierarchy.
  4. Gaya kontrol UI sebagai berikut:

    • Untuk slot1 dan slot2, gaya mereka sebagai kotak 80px X 80px dengan warna latar belakang putih dan sudut bulat. Menyiapkan slot sebagai dua baris dengan dua slot di setiap baris.
    • Untuk object, gaya itu sebagai titik bulat 50px X 50px dengan warna latar belakang hitam.

Untuk instruksi tentang cara menambahkan dan gaya UI kontrol, lihat Pembuat UI.

Anda selesai DragAndDropWindow.uxml harus terlihat seperti berikut:

<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&amp;guid=3d86870c8637c4a3c979a8b4fe0cba4c&amp;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>

Anda selesai DragAndDropWindow.uss harus terlihat seperti berikut:

.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);
}

Tentukan logika drag-and-drop

Untuk menentukan perilaku drag-and-drop, memperpanjang kelas PointerManipulator dan menentukan logika.

  1. Dalam folder DragAndDrop, buat file C# lain yang disebut DragAndDropManipulator.cs.

  2. Buka DragAndDropManipulator.cs.

  3. Tambahkan deklarasi using UnityEngine.UIElements;.

  4. Membuat kelas DragAndDropManipulator memperpanjang PointerManipulator daripada MonoBehaviour, dan lakukan berikut:

  5. Tulis empat metode yang bertindak sebagai callback untuk PointerDownEvents, PointerMoveEvents, PointerUpEvents, dan PointerCaptureOutEvents:

    • PointerDownHandler(): Menyimpan posisi awal target dan pointer, membuat target menangkap pointer, dan menunjukkan bahwa seret sekarang dalam kemajuan.
    • PointerMoveHandler(): Periksa apakah seret dalam kemajuan dan apakah target telah menangkap pointer. Jika keduanya benar, menghitung posisi baru untuk target dalam batas jendela.
    • PointerUpHandler(): Periksa apakah seret dalam kemajuan dan apakah target telah menangkap pointer. Jika keduanya benar, membuat target melepaskan pointer.
    • PointerCaptureOutHandler(): Cek apakah seret sedang berlangsung. Jika benar, pertanyaan akar pohon visual untuk menemukan semua slot, memutuskan slot mana adalah yang paling dekat yang tumpang tindih target, dan menetapkan posisi target sehingga beristirahat di atas slot itu. Mengatur posisi target kembali ke posisi aslinya jika tidak ada slot yang tumpang tindih.
  6. Pada RegisterCallbacksOnTarget(), daftarkan empat callback ini pada target.

  7. Pada UnregisterCallbacksOnTarget(), un-register ini empat callback dari target.

Anda selesai DragAndDropManipulator.cs harus terlihat seperti berikut:

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);
    }
}

Instantiate perilaku drag-and-drop

Untuk mengaktifkan drag-and-drop di jendela kustom, sesaat ketika jendela terbuka.

  1. Pada DragAndDropWindow.cs, tambahkan metode CreateGUI() untuk meluruskan kelas DragAndDropManipulator:

    DragAndDropManipulator manipulator =
        new(rootVisualElement.Q<VisualElement>("object"));
    
  2. Pergi ke bar menu Unity, klik Window > UI Toolkit > Drag And Drop. Di jendela kustom yang dibuka, Anda dapat menyeret objek ke slot apa pun.


Contoh UI Drag-and-drop ditambahkan dalam Unity Panties20212

Buat menu tabbed untuk runtime
Buat daftar dan pemandangan pohon