0

I have an inventory system based largely on this tutorial that I am currently in the process of modifying. OnBeginDrag and OnEndDrag work as expected, but OnDrag, though continuously updating the position of the item, does not display the item. It disappears during OnDrag, and reappears once it has been dropped in its new slot.

Here is my hierarchy:

Unity hierarchy

Of note are the two cameras. The DungeonGroup (and the DungeonUICanvas) use DungeonCamera as far as I can tell. (DungeonGroup was, until recently, a completely different Scene. The OnDrag worked as expected before I integrated the scenes, making me think that a camera issue could be the culprit.)

Here is the code I am currently working with:

public class DragHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public static GameObject item; //itemBeingDragged
    public static Vector3 startPosition;
    public static Transform startParent;

    public void OnBeginDrag(PointerEventData eventData)
    {
        item = gameObject;
        startPosition = transform.position;
        startParent = transform.parent;

        GetComponent<CanvasGroup>().blocksRaycasts = false;

        transform.SetParent(transform.root);
    }

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

    public void OnEndDrag(PointerEventData eventData)
    {
        item = null;

        if (transform.parent == startParent || transform.parent == transform.root)
        {
            transform.position = startPosition;
            transform.SetParent(startParent);
        }

        GetComponent<CanvasGroup>().blocksRaycasts = true;
    }
}
Remy
  • 4,843
  • 5
  • 30
  • 60
Ben I.
  • 1,065
  • 1
  • 13
  • 29
  • `Input.mousePosition` has `z=0` is it possible it is simply too close to the camera? – derHugo Aug 20 '19 at 15:42
  • @derHugo No dice, unfortunately. My `DungeonCamera` has a `z` of `-10` to allow it to display the various `TileGrid`s at `z=0`. (Even so, I just tested it out in case you had solved it, in which case I would have been thanking you and asking you to post an answer so that I could accept it :) – Ben I. Aug 20 '19 at 15:58
  • maybe the reason is `transform.SetParent(transform.root);`? Is `transform.root` still the `Canvas`? UI elements are not visible if in the hierarchy not under a `Canvas` – derHugo Aug 20 '19 at 16:05

1 Answers1

1

Canvas works a little differently than WorldSpace objects. From Unity's UICanvas documentation it states:

UI elements in the Canvas are drawn in the same order they appear in the Hierarchy. The first child is drawn first, the second child next, and so on. If two UI elements overlap, the later one will appear on top of the earlier one.

When you move your transform to root, you are moving the object up higher in the UI hierarchy, resulting in it being "drawn first". Since it's drawn first, all other elements are drawn in front of it, making it "disappear". My recommendation is to .SetParent to a transparent UI gameObject further down in your hierarchy, rather than the root.

Erik Overflow
  • 2,220
  • 1
  • 5
  • 16
  • does it though? I mean isn't `SetParent` causing it to be the last child of that parent thus it would be rendered alsways on top of all the rest? – derHugo Aug 20 '19 at 16:22
  • SetParent does not specify the sibling order in the hierarchy. One could use [Transform.SetAsLastSibling](https://docs.unity3d.com/ScriptReference/Transform.SetAsLastSibling.html) to move the element to the bottom of the UICanvas after parenting it. – Erik Overflow Aug 20 '19 at 16:29
  • I had to create an empty `GameObject` at the end of the UI list to accomplish this, but it worked. That's quite an unexpected way to do things; since the top-to-bottom order within a level does not affect load orderings, it seemed like the system was designed to make such placements irrelevant. Not so! Good catch, and thank you. – Ben I. Aug 20 '19 at 16:43
  • `SetAsLastSibling` to me then sounds more desirable than adding stuff to the hierarchy – derHugo Aug 20 '19 at 16:51
  • While it is an immediate solution and works here, it makes it much harder to place the object in an exact place in the hierarchy. Say for example you have a "HUD" and an "Inventory Panel". When you drag something in the inventory panel it should move in front of the panel, but maybe not your HUD. If they share the same UI Canvas, you would need to specify the specific child index to make this possible. Or you could have a transform do the same. – Erik Overflow Aug 20 '19 at 17:39
  • @derHugo It doesn't fix the problem, as items in the top panel (Equipped) then sink below items in the lower panel (Unequipped). Though setting the parent to LastObjectInList (my creatively named GameObject) now causes the items to also stick to the labels, like "Weapon" or "Ground". *sigh* – Ben I. Aug 20 '19 at 18:02
  • @ErikOverflow ^ ^ ^ Any ideas about how to fix the original problem without creating this new problem? (Or should I make a new question at this point.) – Ben I. Aug 20 '19 at 18:03
  • I don't quite understand the problem you're running into with labels. I would not use the SetAsLastSibling approach, and instead would just use a new GameObject placed properly in your UI hierarchy that can act as your "dragged objects" holder. – Erik Overflow Aug 20 '19 at 18:11
  • @ErikOverflow The problem I was having was that both the Equipped and Unequipped panel contain slots, but one panel was always above the other. For whatever reason, parenting the items outside of the panels entirely was causing them to find undesired GameObject (as in, not `Slot`s) to latch onto when I unclicked. I noticed that moving within a panel was fine, and moving from Sibling 1 to Sibling 0 was also always fine, so my eventual fix was to just switch the order of the panels themselves during `OnBeginDrag` when needed, and so far, no more problems. – Ben I. Aug 20 '19 at 19:45