【问题标题】:Drag and Drop In ScrollRect (ScrollView) in Unity3D在 Unity3D 中拖放到 ScrollRect (ScrollView)
【发布时间】:2017-06-13 07:56:55
【问题描述】:

我想在滚动视图的内容上实现拖放。

问题是当您尝试在滚动视图中拖动项目时,您无法滚动视图。

首先,我尝试通过IDragHandler、IBeginDragHandler、IEndDragHandle和IDropHandler接口实现拖放。乍一看,它工作得很好,但问题是你不能滚动 ScrollRect。

我认为问题是因为覆盖,当我使用与滚动矩形相同的事件触发器时,父级无法正常工作。

所以在那之后,我自己思考并通过IPointerDown,IPointerUp接口和ScrollRect中保持拖拽UI的特定时间来实现它,如果你没有在特定时间保持它,滚动效果很好。

但问题是通过启用我在 OnDrag、OnBeginDrag 和 OnEndDrag 函数之前编写的 DragHandler 脚本在保持时间结束时不起作用。

首先我想知道有什么方法可以调用这些函数?

二是有什么方法可以不使用拖拽界面实现拖拽UI?

拖动处理程序:

using System;
using UnityEngine;
using UnityEngine.EventSystems;

public class DragHandler : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    public static GameObject itemBeingDragged;

    private Vector3 startPos;

    private Transform startParent;

    DragHandler dragHandler;

    public void Awake()
    {
        dragHandler = GetComponent<DragHandler>();
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("Begin");
        itemBeingDragged = gameObject;
        startPos = transform.position;
        startParent = transform.parent;
    }

    public void OnDrag(PointerEventData eventData)
    {
        Debug.Log("Drag");
        transform.position = Input.mousePosition;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("End");
        itemBeingDragged = null;
        if (transform.parent == startParent)
        {
            dragHandler.enabled = false;
            transform.SetParent(startParent);
            transform.position = startPos;
        }
    }
}

滚动矩形控制器:

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

public class ScrollRectController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    public float holdTime;
    public float maxVelocity;

    private Transform scrollRectParent;

    private DragHandler dragHandler;

    private ScrollRect scrollRect;

    private float timer;

    private bool isHolding;

    void Awake()
    {
        scrollRectParent = GameObject.FindGameObjectWithTag("rec_dlg").transform;
        dragHandler = GetComponent<DragHandler>();
        dragHandler.enabled = false;
    }

    // Use this for initialization
    void Start()
    {
        timer = holdTime;
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void OnPointerDown(PointerEventData eventData)
    {
        Debug.Log("Down");
        scrollRect = scrollRectParent.GetComponent<ScrollRect>();
        isHolding = true;
        StartCoroutine(Holding());
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        Debug.Log("Up");
        isHolding = false;
    }

    IEnumerator Holding()
    {
        while (timer > 0)
        {
            //if (scrollRect.velocity.x >= maxVelocity)
            //{
            //    isHolding = false;
            //}

            if (!isHolding)
            {
                timer = holdTime;
                yield break;
            }

            timer -= Time.deltaTime;
            Debug.Log(timer);
            yield return null;
        }

        dragHandler.enabled = true;
        //dragHandler.OnBeginDrag();
    }
}

插槽:

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

public class Slot : MonoBehaviour, IDropHandler
{
    public void OnDrop(PointerEventData eventData)
    {
        DragHandler.itemBeingDragged.transform.SetParent(transform);
    }
}

【问题讨论】:

  • 呃。拖放 UI 元素对于新 UI 来说是一个巨大的痛苦。这是不容易的一件事。我现在在这台计算机上没有使用它的项目,但基本上你为 每个 可拖动项目创建了一个零大小的滚动视图,并将“clamp”模式设置为“unbound”。然后这些滚动视图嵌入到锚点的父容器(可以是另一个滚动视图)中。每项滚动视图的内容对象的变换是用于定位的变换。 IIRC。如果您愿意,请查看github.com/Draco18s/IdleArtificer
  • 嘿,我已经检查了链接并下载了项目,挖掘了它,但没有发现任何有用的和与滚动视图中的拖放相关的东西,我想我需要澄清一些事情,关于在滚动视图中拖放,我的意思是将项目从滚动视图拖到滚动视图之外的其他地方。关于你说的小费,我不认为它解决了我的问题。
  • 对不起。我用完了字符。你需要下载它。打开主场景。在“游戏可见”区域的左侧,我保留了预制件的副本。寻找上面/旁边有箭头的那个。那个,当在游戏中被实例化时,是可拖动的。您需要检查转换层次结构(包括 RectTransform 枢轴和锚点)。它们由 CraftingManager.cs 实例化,第 210 行,而第 219 行控制它在屏幕上的放置位置。
  • 嘿伙计,我在您的项目中使用了 BuildingGridItem 预制件。我将这些预制件添加到滚动视图中并测试拖放和滚动,但问题是当鼠标指针位于 BuildingGridItem 上时,滚动滚动视图与我的问题不同。
  • 您可以拖动一个项目您扫描滚动父滚动视图。你不能同时做这两件事,这是没有意义的。

标签: unity3d drag-and-drop scrollview eventtrigger scrollrect


【解决方案1】:

答案:

我写了一些代码来处理scrollRect(scrollView)中的拖放而不使用DragHandler接口。

DragHandler:

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

public class DragHandler : MonoBehaviour, IPointerExitHandler
{
    public static GameObject itemBeingDragged;

    public static bool isCustomerDragged;

    public Transform customerScrollRect;
    public Transform dragParent;

    public float holdTime;
    public float maxScrollVelocityInDrag;

    private Transform startParent;

    private ScrollRect scrollRect;

    private float timer;

    private bool isHolding;
    private bool canDrag;
    private bool isPointerOverGameObject;

    private CanvasGroup canvasGroup;

    private Vector3 startPos;

    public Transform StartParent
    {
        get { return startParent; }
    }

    public Vector3 StartPos
    {
        get { return startPos; }
    }

    void Awake()
    {
        canvasGroup = GetComponent<CanvasGroup>();
    }

    // Use this for initialization
    void Start()
    {
        timer = holdTime;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                //Debug.Log("Mouse Button Down");
                scrollRect = customerScrollRect.GetComponent<ScrollRect>();
                isPointerOverGameObject = true;
                isHolding = true;
                StartCoroutine(Holding());
            }
        }

        if (Input.GetMouseButtonUp(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                //Debug.Log("Mouse Button Up");
                isHolding = false;

                if (canDrag)
                {
                    itemBeingDragged = null;
                    isCustomerDragged = false;
                    if (transform.parent == dragParent)
                    {
                        canvasGroup.blocksRaycasts = true;
                        transform.SetParent(startParent);
                        transform.localPosition = startPos;
                    }
                    canDrag = false;
                    timer = holdTime;
                }
            }
        }

        if (Input.GetMouseButton(0))
        {
            if (EventSystem.current.currentSelectedGameObject == gameObject)
            {
                if (canDrag)
                {
                    //Debug.Log("Mouse Button");
                    transform.position = Input.mousePosition;
                }
                else
                {
                    if (!isPointerOverGameObject)
                    {
                        isHolding = false;
                    }
                }
            }
        }
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isPointerOverGameObject = false;
    }

    IEnumerator Holding()
    {
        while (timer > 0)
        {
            if (scrollRect.velocity.x >= maxScrollVelocityInDrag)
            {
                isHolding = false;
            }

            if (!isHolding)
            {
                timer = holdTime;
                yield break;
            }

            timer -= Time.deltaTime;
            //Debug.Log("Time : " + timer);
            yield return null;
        }

        isCustomerDragged = true;
        itemBeingDragged = gameObject;
        startPos = transform.localPosition;
        startParent = transform.parent;
        canDrag = true;
        canvasGroup.blocksRaycasts = false;
        transform.SetParent(dragParent);
    }

    public void Reset()
    {
        isHolding = false;
        canDrag = false;
        isPointerOverGameObject = false;
    }
}

对这段代码的一些解释:

  1. 您的可拖动 UI 元素需要难以处理的选项,对我来说,我使用了按钮。
  2. 您需要将此脚本附加到您的可拖动项目。
  3. 您还需要添加 Canvas Group 组件。
  4. customerScrollRect 是您的项目的 ScrollRect 父项。
  5. dragParent 可以是一个空的 GameObject,由于视口的掩码而被使用。

插槽:

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

public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    bool isEntered;

    public void OnPointerEnter(PointerEventData eventData)
    {
        isEntered = true;
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isEntered = false;
    }

    void Update()
    {
        if (Input.GetMouseButtonUp(0))
        {
            if (isEntered)
            {
                if (DragHandler.itemBeingDragged)
                {
                    GameObject draggedItem = DragHandler.itemBeingDragged;
                    DragHandler dragHandler = draggedItem.GetComponent<DragHandler>();
                    Vector3 childPos = draggedItem.transform.position;
                    //Debug.Log("On Pointer Enter");
                    draggedItem.transform.SetParent(dragHandler.StartParent);
                    draggedItem.transform.localPosition = dragHandler.StartPos;
                    draggedItem.transform.parent.SetParent(transform);
                    draggedItem.transform.parent.position = childPos;
                    isEntered = false;
                }
            }
        }
    }
}

对这个脚本的一些解释:

1.将脚本附加到放置的项目上。

【讨论】:

  • 这个答案超级有用!可以改进的一件事是保留/恢复您的 childIndex,以便元素不会被扔到 scrollRect 的底部。
【解决方案2】:

这个问题最简单的解决方案实际上是手动调用ScrollRect的事件IF用户没有按下足够长的时间使用ExecuteEvents.Execute。此解决方案的附加代码最少。

using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class DragAndDropHandler : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IDragHandler, IBeginDragHandler, IEndDragHandler
{
    public bool Draggable { get; set; }

    private bool draggingSlot;

    [SerializeField] private ScrollRect scrollRect;

    public void OnPointerDown(PointerEventData eventData)
    {
        if (!Draggable)
        {
            return;
        }

        StartCoroutine(StartTimer());
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        StopAllCoroutines();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        StopAllCoroutines();
    }

    private IEnumerator StartTimer()
    {
        yield return new WaitForSeconds(0.5f);
        draggingSlot = true;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.beginDragHandler);
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (draggingSlot)
        {
            //DO YOUR DRAGGING HERE
        } else
        {
            //OR DO THE SCROLLRECT'S
            ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.dragHandler);
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        ExecuteEvents.Execute(scrollRect.gameObject, eventData, ExecuteEvents.endDragHandler);
        if (draggingSlot)
        {
            //END YOUR DRAGGING HERE
            draggingSlot = false;
        }
    }
}

【讨论】:

  • 使用 ExecuteEvents 是一个绝妙的答案!还可以轻松选择性地打开和关闭滚动拖动(我想将项目从滚动矩形拖到我的场景中)。请注意,legacy documentation 似乎比 latest documentation 更有帮助。
【解决方案3】:

我设法找到了一个更简单的解决方案。 也许它必须根据特殊需要进行定制,但如果你把这个脚本放在你的项目上并进行设置,它应该可以工作。

预制件并不容易,但我让控制器来完成。 我在指定的 GameObject 中找到所有 UIElementDragger 并以编程方式添加所需的 GO 实例。

但如果您不使用预制件,可以直接使用此代码。

using UnityEngine;
using UnityEngine.EventSystems;

public class UIElementDragger : MonoBehaviour, IPointerUpHandler, IPointerDownHandler
{
    /// <summary>
    /// Offset in pixels horizontally (positive to right, negative to left)
    /// </summary>
    [Range(40, 100)]
    public float offsetX = 40;

    /// <summary>
    /// Offset in pixels vertically (positive to right, negative to left)
    /// </summary>
    [Range(40, 100)]
    public float offsetY = 40;

    /// <summary>
    /// The Panel where the item will set as Child to during drag
    /// </summary>
    public Transform parentRect;

    /// <summary>
    /// The GameObject where the item is at start
    /// </summary>
    public Transform homeWrapper;

    /// <summary>
    /// The Object where the mouse must be when pointer is up, to put it in this panel
    /// </summary>
    public Transform targetRect;

    /// <summary>
    /// The GameObject where the item should live after dropping
    /// </summary>
    public Transform targetWrapper;

    private int siblingIndex;
    private bool dragging;

    private void Start()
    {
        siblingIndex = transform.GetSiblingIndex();
    }

    private void Update()
    {
        if (dragging)
        {
            transform.position = new Vector2(Input.mousePosition.x + offsetX, Input.mousePosition.y + offsetY);
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        transform.parent = parentRect;
        dragging = true;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (eventData.pointerCurrentRaycast.gameObject.transform.IsChildOf(targetRect))
        {
            transform.parent = targetWrapper;
        }
        else
        {
            transform.parent = homeWrapper;
            transform.SetSiblingIndex(siblingIndex);
        }

        dragging = false;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多