0
\$\begingroup\$

I'm trying to make the UI part of an inventory system which will involve the ability to move items from one slot to another, animating the item sprite going from one slot to the other.

Item slot prefabs initially had a UI Image component whose sprite property I used to display the item. An issue I had with that is that when moving items from one slot to another, the sprite would either appear on top or behind other items' sprites depending on (I assume) the hierarchy order of the slots to which they belong.

I tried looking around into how I could dynamically modify UI Image or sprite render priority, but solutions I found would either involve:

  • Using a SpriteRenderer, which doesn't seem to be what UI Image use, meaning I have to add one in the prefab, and if I do that I might as well do away with UI Images in the first place and exclusively use them -> This doesn't work, and it seems that SpriteRenderers aren't made to work in a Canvas? From what I've tried, it seems like the RectTransform used by Images and Transform used by the SpriteRenderer don't use the same coordinate system: if I give the same position to both, they would appear in different places, and the sprite would most likely be wayyy offscreen, unless I instead give it the same coordinates as another object visible in the scene.

  • Using SortingGroup and sorting layers, which doesn't seem to work either for UI Images.

  • Modifying the render order by modifying the order of objects in the hierarchy. This last one would I assume works, but feel kinda botchey and I can't imagine that relying on hierarchy order for drawing order couldn't lead to issues further down the line. Since currently each image is currently a child of an item slot gameobject, I can already see that it would likely also reorder the item slots themselves in my GridLayoutGroup, which would require further botching or rethinking everything to fix.

  • Using multiple canvases so that one can give them different render priority. I'm not sure how it would go exactly, maybe two of the same size layered one and temporarily moving item slots from one canvas to the other for the duration of the sprite's movement? But this also feel very botchy.

I will eventually settle in for the hierarchy order method, but I feel like there is no way there isn't a better solution for this, unless that is my inexperience speaking and that solution is actually good.

My question is this: While not asking for a "best" solution, what would be one proper way to achieve this?

I am aware the formulation is kinda loaded, and maybe "do it by altering hierarchy order" could be a proper solution.

The version of Unity being used is 2021.2.2f1. If something is unclear, I can provide more information or reformulate.

\$\endgroup\$
4
  • \$\begingroup\$ Remember that the item that sits in the slot and the item that gets dragged between slots do not have to be the same object, just choreographed to appear so to the player. \$\endgroup\$
    – DMGregory
    Commented Jan 2 at 21:00
  • \$\begingroup\$ Ah, yes. Heavy tunnel vision on my end for that one. An image permanently located below my item slots in the hierarchy would do. Thanks! \$\endgroup\$
    – Uretki
    Commented Jan 2 at 21:15
  • 1
    \$\begingroup\$ If you solve your problem using that trick, it could be useful to share a walkthrough of your solution as an answer below. \$\endgroup\$
    – DMGregory
    Commented Jan 2 at 21:40
  • \$\begingroup\$ @DMGregory No code, but I feel like it's explanatory enough, I don't know. \$\endgroup\$
    – Uretki
    Commented Jan 14 at 18:32

1 Answer 1

2
\$\begingroup\$

I don't know whether this solution is good or not, but here is what I did. Note that this could be simplified if you had the garantee that only one item could move at once. I do not know how viable this solution would be for a different usecase, for instance if numerous animations are expected to run very often.

I created a new Monobehavior class ItemSlotAnimatorManager, along with a regular class ItemSlotAnimator.

The logic for moving item images around was moved to ItemSlotAnimators. One of those objects can only handle one animation at a time.

The ItemSlotAnimatorManager in the scene contains a pool of animators which it populates in Awake. For each animator it creates, it also instanciates a copy of my Item Sprite prefab, and passes its Image component to the animator which will use it.

The inventory UI can then use the manager to request an animation to be performed.

The manager keeps two lists of animators: One for idle animators, another for animators in use. Its Update loops through animators currently in use and calls their method to update them. The update method of animators returns true when the animation is over, which allows the manager to know which animators need to be returned to the idle animator list.

The GameObject which contains the ItemSlotAnimatorManager only needs to be placed below your inventory in your UI.

This solution will be limited by the number of animators in the pool, so this needs to be kept in mind - for instance by preventing the spamming of actions that trigger the animations, having a bit more animators than you think would be reasonably necessary, and defining behavior in case no animators are available.

Notes for decisions I took:

  • Handling separate lists for idle and active animators, so you do not loop through idle animators for no reason during each Update, and you can just pop an idle animator when one is needed without having to search.
  • Using a pool to prevent the creation and destruction of animator objects every time an animation occurs and finishes
  • Maybe I could do away with the animator class and have one object handle as many animations as it wants, I have no idea if this would be more efficient, and I am not bothering.
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .