[Solved] Trying to detect a drop directly on the canvas

I am essentially trying to drop onto nothing.

I’m recreating a classic fRPG Inventory. I have a reliable drag and drop system that detects icon drops onto targets using implementations of IDropHandler and OnDrop.

My UI resides in a full screen canvas.

I want to be able to drop an icon into “nothing” (essentially the empty canvas), detect the drop and bring up my modal window with a “Do you want to destroy / drop ?” [Yes] [No] Message. (Modal window is working fine, btw).

What’s not working are any of my attempts to detect a drop onto the canvas, nor have I done well at finding a way to check what’s under the pointer to analyse that data to trigger the modal window.

One thought was the IsPointerOverGameObject (iirc the name), but as the canvas is a UI Element and it’s fullscreen, my pointer will always return true with that one.

Any thoughts on the best way to check if I’ve done a PointerUp or Drop over nothing but the canvas?

Hum… some progress:

I discovered that PointerEventData has hovered.

That means I can do this:

    public void OnPointerUp (PointerEventData data)
    {
        // Some Code

            List<GameObject> hoveredList = data.hovered;
            foreach (var GO in hoveredList)
            {
                Debug.Log("Hovering over: " + GO.name);
            }
        // More Code
    }

3426539--270568--f621c108fc410aeae860e3f5f94cbd2d.png

This, however, just gives me a list of all of the GameObjects in the hierarchy under the pointer, and when it’s not over a UI Element (eg: the UI “Void”) where I want to detect a drop outside the UI, I just get a list of all of the walls and floors and chests in the scene under the pointer. I’m not sure how I can sort this into valid data to prove there is no UI Elements there, so therefore it’s not in the UI, so therefore I can destroy it…

Better, still not optimal.

Anyone else have any thoughts?

I’m fairly positive that OnDrop occurs before OnEndDrag. This means that you can unparent a UI object when you drag it, and re-parent for a valid drop. If OnEndDrag occurs and the parent is the canvas, you can execute your logic at that time.

Poop. Good idea. Sadly, the UI Object is always parented directly to the canvas.

What I’m doing is using a “Dragging Item” that I use when an icon/item is “grabbed” from a slot, rather than moving the icon/item clicked on itself. [edit] I transfer all of the necessary data to the dragging item: its icon, a reference to the item, etc…[/edit] This way, I can leave the “slot” where it is (and this is important as it’s in a layout group) is and just make it an “empty slot”. This means I can’t test who’s the parent or a parent change.

3428175--270764--DraggingObject.png
*simplified hierarchy

The “OnDrops” are handled independently by whatever received the “OnDrop” event, so if I drop on another slot or another panel, they have their own “OnDrop” to handle the event. Dropping on the inventory panel, but missing a slot, will try to add the item generically to the inventory. Dropping on as slot will try to add at that slot, or add to stack or swap item … or whatever the logic needs to be. This means that the “void” or base canvas needs to detect the drop independently.

I’m currently experimenting on the rout of making a “drop catcher” layer that resided under the entire UI.

My problem there is that i need to click into the scene, and an invisible image layer that can detect the drop also blocks all raycasts into the scene. If I disable “blocks raycasts” or if I make “raycast target = false” then the UI Element doesn’t detect the event!

Bother!

The solution I came up with is here:

Hmm… I did a similar thing (with a ‘dragged’ object) representing inventory items, for instance. However, instead of what you did, I still ran “OnDrag” on the slot, but simply moved the draggable item in its place (never moved the inventory slot item). This way, I didn’t really transfer much - just the icon, I think?

You could maybe have something like:

void OnDrop(PointerEventData data) {
   InvItem ii = data.pointerDrag.GetComponent<InvItem>();
   if(ii) {
       ii.validDrop = true;
     }
  }
void OnEndDrag(PointerEventData data) {
   if(!validDrop) // dropped in void
   validDrop = false; // just reset for future use, doesn't matter if it was valid or not at this point, I think.
  }

Okay, so I’m going to leave that there. I think that might work… however, I just had another thought after I wrote that. What if you destroy the dragged image (which I assume you do ‘OnDrop’) if it’s successful, and then set it to null there… again, if successful.
Now, if OnEndDrag, the draggable image isn’t null, you know you’re in the void?

This second idea would avoid that extra bit of saving a variable.

Let me know if that makes sense, or if I wrote it too quickly without thinking it through lol

1 Like

Doesn’t sound so bad…

Oh! (Crosspost!)

That’s a cool solution! I may have a rethink.

Thanks!

I get what you’re doing there now.

:slight_smile: Of course, just checking “activeSelf” for the dragged image is cool, too… if you re-use that image/game object.