2

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

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

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

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

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

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

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

其次,有什么方法可以在不使用拖动接口的情况下实现拖放 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);
    }
}
4

3 回答 3

7

回答:

我编写了一些代码来处理 scrollRect(scrollView) 中的拖放而不使用 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.将脚本附加到放置的项目上。

于 2017-06-15T04:29:20.133 回答
3

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

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;
        }
    }
}
于 2020-05-08T04:21:56.533 回答
1

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

预制件并不容易,但我让控制器来做这个。我在指定的 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;
    }
}
于 2019-07-01T20:37:55.850 回答