11

I have a script that handles dragging of an items from and to a given slot. But i want to add a function to stop dragging of a specific items. i think the best place to do is in the OnBeginDrag method, but cant seem to figure a way to stop/cancel the drag event itself, here is a bit of my code

public class SlotBehaviour : MonoBehaviour, IDropHandler, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IDragHandler, IEndDragHandler,IPointerClickHandler
{
    public void OnBeginDrag(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left)
        {
            return;
        }
        if (this.Empty) return;
        var canvas = imageItem.canvas;
        if (canvas == null) return;
        GUIManager.mouseBusy = true;
        // We have clicked something that can be dragged.
        // What we want to do is create an icon for this.
        m_DraggingIcon = new GameObject("icon");

        m_DraggingIcon.transform.SetParent(canvas.transform, false);
        m_DraggingIcon.transform.SetAsLastSibling();

        var image = m_DraggingIcon.AddComponent<Image>();
        // The icon will be under the cursor.
        // We want it to be ignored by the event system.
        m_DraggingIcon.AddComponent<IgnoreRaycast>();

        image.sprite = imageItem.sprite;
        image.rectTransform.sizeDelta = imageItem.rectTransform.sizeDelta;


        m_DraggingPlane = transform as RectTransform;

        SetDraggedPosition(eventData);

    }

    public void OnDrag(PointerEventData data)
    {
        if (m_DraggingIcon != null)
            SetDraggedPosition(data);
    }

    private void SetDraggedPosition(PointerEventData data)
    {
        if (data.pointerEnter != null && data.pointerEnter.transform as RectTransform != null)
            m_DraggingPlane = data.pointerEnter.transform as RectTransform;

        var rt = m_DraggingIcon.GetComponent<RectTransform>();
        Vector3 globalMousePos;
        if (RectTransformUtility.ScreenPointToWorldPointInRectangle(m_DraggingPlane, data.position, data.pressEventCamera, out globalMousePos))
        {
            rt.position = globalMousePos;
            rt.rotation = m_DraggingPlane.rotation;
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if (m_DraggingIcon != null)
        {

            Destroy(m_DraggingIcon);
        }
        GUIManager.mouseBusy = false;
        //if you drop it somewhere where its not wanted(or just nowhere)
        if (eventData.used == false)
        {
            if (eventData.pointerCurrentRaycast.gameObject == null)// if its nowhere offer to drop it on ground
            {
                GUIManager.instance.DropItem((int)ItemsDatabase.container[containerID].items[indexInContainer]);
            }
        }
    }
}

I tried returning the method earlier but it doesnt do anything, probably need to do something with the event data...i will be gratefull if you tell me how to deal with it.

Chris McFarland
  • 6,059
  • 5
  • 43
  • 63
Ivan Kuzev
  • 138
  • 1
  • 1
  • 7
  • NOTE - here's an extremely full, an elegant, solution for drag/drop .. enjoy http://stackoverflow.com/a/37473953/294884 – Fattie May 27 '16 at 18:49

3 Answers3

48

Just using "return" doesn't cancel anything.

Instead, you can modify the PointerEventData info that is passed into the OnBeginDrag function - specifically, set pointerDrag to null. This will cancel the drag:

eventData.pointerDrag = null;
Andrew
  • 481
  • 4
  • 3
  • 7
    This should be marked as correct answer. The other suggestion about returning from the event method is childish! – Xtro Apr 13 '17 at 15:48
  • 3
    This answer was posted exactly two years after the accepted answer, but this is actually the correct answer. As stated, setting `pointerDrag` to `null` does, in fact, prevent the OnDrag and OnEndDrag events from even being raised. Thanks! – monkey0506 Jul 20 '17 at 08:26
  • 1
    Depressingly I've found a situation where this does NOT work - I wonder if there's a bug in 2020 ? – Fattie Nov 09 '20 at 14:09
-1

setting null seems to NOT work in 2020 :/

Alert, seems to not work in 2020. OnDrag still gets called.

Here's a flag solution that works. Note you have to logically use mouse-down? but there's no other realistic way :/

using UnityEngine;
using UnityEngine.EventSystems;

public class CancellableDragBase : MonoBehaviour,
    IBeginDragHandler, IDragHandler, IEndDragHandler,
    IPointerUpHandler
{
    bool cancelDrag = false;

    public virtual void OnBeginDragCancellable() {}
    public virtual void OnDragCancellable() {}
    public virtual void OnEndDragCancellable() { }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (cancelDrag)
        {
            return;
        }
        OnBeginDragCancellable();
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (cancelDrag)
        {
            return;
        }
        OnDragCancellable();
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        if (cancelDrag)
        {
            cancelDrag = false;
            return;
        }
        OnEndDragCancellable();
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (cancelDrag)
        {
            cancelDrag = false;
            return;
        }
    }

    public void CancelDragIfAny()
    {
        if (Input.GetMouseButton(0))
        {
            cancelDrag = true;
        }
        else
        {
        }
    }
}

What a hassle.

Fattie
  • 27,874
  • 70
  • 431
  • 719
  • `eventData.pointerDrag = null;` is working for me in 2020.1.17f1 – Dustin Graham Dec 30 '20 at 17:57
  • 1
    @DustinGraham ; thanks ... hmm; definitely not working here if the mouse is still down. ondrag, onenddrag, and onpointerup are all still incorrectly called. I will investigate more! – Fattie Dec 30 '20 at 17:59
-6

You may create flag (for example, IsDragable). For items which you donn't want to drag&drop you have to return from drag event handlers:

public class DragHangler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {

public bool IsDragable;

#region IBeginDragHandler implementation
public void OnBeginDrag (PointerEventData eventData)
{
    if (!IsDragable)    return;

    Debug.Log ("OnBeginDrag:Do something");
}
#endregion

#region IDragHandler implementation

public void OnDrag (PointerEventData eventData)
{
    if (!IsDragable)    return;

    Debug.Log ("OnDrag: Do something");
}

#endregion

#region IEndDragHandler implementation

public void OnEndDrag (PointerEventData eventData)
{
    if (!IsDragable) return;

    Debug.Log ("OnEnd: Do something");
}

#endregion
}

Other solution is to disable BlockRaycast in CanvasGroup component.

acoolaum
  • 2,132
  • 2
  • 15
  • 24
  • 1
    well that is what i am doing.but it does not really stop the object's OnDrag and on OnEndDrag events. or the OnDrop(called on the destination). – Ivan Kuzev Feb 26 '15 at 15:54
  • There is no problem here. You may check IsDraggable flag in OnDrop event. And do nothing if it is false. – acoolaum Mar 06 '15 at 14:55
  • Thank you! I like BlockRaycast much better approach since this also disable all triggers (like animations) that depends on Drag event for that GameObject, while DragHandler is not helping disabling events and still triggers firing (animations playing). – Qorbani Jan 01 '16 at 18:42
  • This doesn't answer the OP, it offers a way to circumvene the fact that these events are still raised. [Andrew's answer](https://stackoverflow.com/a/42288136/829728) should be the accepted one as it actually prevents further calls to OnDrag and OnEndDrag... so you *don't* have to check any flag in it. – psycho Dec 14 '19 at 20:48