Search Unity

[UI Image] How to change render order between Parent and Child?

Discussion in 'UGUI & TextMesh Pro' started by PoL231, Sep 14, 2014.

  1. PoL231

    PoL231

    Joined:
    Jun 27, 2014
    Posts:
    148
    In this configuration:

    Canvas
    + Parent Image
    ++ Child Image


    "Parent Image" will be rendered behind the "Child Image".

    Is it possible to change the order of rendering in such a situation?

    I need to render the "Parent Image" in front of "Child Image" without changing the layout.
     
  2. secondbreakfast

    secondbreakfast

    Joined:
    Jan 5, 2013
    Posts:
    98
    No, it's not possible. Instead make your parent an object that doesn't have a graphic and is an empty recttransform and move the image in the parent to a child object. Then you can have it be in the order you want.
     
  3. PoL231

    PoL231

    Joined:
    Jun 27, 2014
    Posts:
    148
    Hi.

    It is some kind of solution (rather workaround), but what if the layout was something like this:

    Canvas
    +Image 1
    ++Image 2
    +++Image 3
    ++++Image 4


    I'm implementing an animation system based on Adobe After Effects animations.

    At this moment the most reasonable solution would be to have the layout like this:

    Canvas:
    +Image 1
    +Image 2
    +Image 3
    +Image 4


    and manual calculate transformation matrix for each layer as if they were parented as shown earlier.

    Obviously this isn't the best solution, especially when it comes to overhead of additional calculations of transformation matrices, but the most universal when it comes to setting the order of layers regardless of their place in the hierarchy.

    I was hoping that Unity has a way to change the order of rendering UI Images regardless of the place in the hierarchy of objects.
     
  4. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    i think i do something similar

    i use z to determine z
    and then sort children by z
    maybe a better way



    Code (CSharp):
    1. using System.Linq;
    2. using UnityEngine;
    3. public static class GameObjectExtensions
    4. {
    5.     // public static void SortChildren(this GameObject gameObject)
    6.     // {
    7.     //     var children = gameObject.GetComponentsInChildren<Transform>(true);
    8.     //     var sorted = from child in children
    9.     //                  orderby child.gameObject.activeInHierarchy descending, child.localPosition.z descending
    10.     //                  where child != gameObject.transform
    11.     //                  select child;
    12.     //     for (int i = 0; i < sorted.Count(); i++)
    13.     //         sorted.ElementAt(i).SetSiblingIndex(i);
    14.     // }
    15.     public static void SortChildren(this GameObject o)
    16.     {
    17.         var children = o.GetComponentsInChildren<Transform>(true).ToList();
    18.         children.Remove(o.transform);
    19.         children.Sort(Compare);
    20.         for (int i = 0; i < children.Count; i++)
    21.             children[i].SetSiblingIndex(i);
    22.     }
    23.     private static int Compare(Transform lhs, Transform rhs)
    24.     {
    25.         if (lhs == rhs) return 0;
    26.         var test = rhs.gameObject.activeInHierarchy.CompareTo(lhs.gameObject.activeInHierarchy);
    27.         if (test != 0) return test;
    28.         if (lhs.localPosition.z < rhs.localPosition.z) return 1;
    29.         if (lhs.localPosition.z > rhs.localPosition.z) return -1;
    30.         return 0;
    31.     }
    32. }
    33.  
     
  5. secondbreakfast

    secondbreakfast

    Joined:
    Jan 5, 2013
    Posts:
    98
    The solution is still the same. There's no need to do anything manual. It behaves as if the are directly nested. For that case do this

    + group 1
    ++ image 1
    ++ group 2
    +++ image 2
    +++ group 3
     
  6. Archie888

    Archie888

    Joined:
    May 17, 2014
    Posts:
    41
    It seems painful that there is no other way to set the render order. I think that the best principle for any kind of development setup is to give the devs as much freedom and choices as possible. You can never predict what kind of need for solutions they have, and you should not enforce a philosophy or set of 'best practices' for anything -- there are always valid arguments in defense for any type of solutions picked. Besides, it is a subjective world, and everyone has their own philosophies on how to see things.

    See for example my scenario: I have a vertical layout, that contains many prefab UI object. Those prefab UI objects themselves have also a vert. layout, and contain the following setup: They have two main objects vertically laid out: below is a text object, which can stretch downwards, making the whole prefab stretch, and the parent vertical layout also. Above that text is a container, which acts as an empty spacing above the text objects in the recurring prefab setup, and also it contains two freely floating image objects, that form a profile picture, associated with the text. The other is a shadow image of the profile picture below, the other the profile image on top.

    My problem is that I want a) the profile picture to be contained in the container above, which makes sense conceptually and spatially b) I want the profile picture to be on top of the text object slightly (not on top of the text, just few pixels on top of the box to have a nice look to the whole thing)

    Now -- if I could simply have those pics on above the text box by setting it so, all would be great. But no, this is not possible currently. The text box is below the spacing in the vertical layout hierarchy, in order to be laid out correctly, and for this reason, it also happens to be rendered on top of anything else, including freely hanging pictures contained in the spacing object.

    Attached is a super-hastily drawn, nasty example sketch of what I am after. Sorry folks, it was all I had time to do.

    Edit: Ok, I solved it by setting the profile pic imgs as a free-floating childs of the text box. However, this is not what I conceptually wanted to do.
     

    Attached Files:

    Last edited: Mar 28, 2015
    Luxiidev, ShuTheWise and Deleted User like this.
  7. jonc113

    jonc113

    Joined:
    Mar 10, 2013
    Posts:
    22
    You can set Sorting Layer and Order on a Canvas, so juggling multiple canvases is an awkward, but possible solution. I found this script on Unity Answers (can't find the original link) and there are similar scripts elsewhere. It exposes sorting layer/order for ALL mesh renderers! With this, you can put a non-UI image or text in the mix and layer it however you please.
     

    Attached Files:

    TechSupportIncoming1 likes this.
  8. TechSupportIncoming1

    TechSupportIncoming1

    Joined:
    Mar 13, 2014
    Posts:
    4

    Hey rakkarage, would you please tell me how you implemented horizontal traversing using the arrows without using a scrollbar? I am using a Scroll Rect with a mask and I need to traverse the GO's inside the scrollview using my custom buttons rather than a utilizing a horizontal scrollbar.
     
  9. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    hook your prev and next up to functions like
    Code (CSharp):
    1. public void Prev()
    2. {
    3.     Go(GetCurrent() - 1);
    4. }
    5. public void Next()
    6. {
    7.     Go(GetCurrent() + 1);
    8. }
    9.  
    i am not using a scroll rect but i guess you just offset by (index * width) or
     
  10. Deleted User

    Deleted User

    Guest

    I agree with Archie888 that the current system relying only on child order is not sufficient. Vertical/Horizontal Layout Group rely on child order to drive layout, so I can't programmatically change the child order without also changing the layout. But some children in my Vertical Layout Group include drop down menus; I made a nice Spinner implementation that brings up the menu but it always appears behind the next panel below. The only solution is to (horrendously) break encapsulation e.g. create an overlay panel two parents above the Spinner that they magically use to show the menu, or give up on using the Vertical/Horizontal Layout and just lock all panels to fixed coordinates so I can reorder the children without changing layout. As the list members are dynamic with varying heights that is also painful.

    Best solution I can see would be a way to set some type of UI render (draw) order per game object without having to do it at the canvas level.
     
    milesvdw and Menion-Leah like this.
  11. NetEeyore

    NetEeyore

    Joined:
    Nov 7, 2016
    Posts:
    1
    I know this is an old thread, but in case someone else is looking for a simple solution, I just changed the spacing to a negative number the size of my card, plus however much I wanted them to overlap.

    for example, my cards (the objects I wanted in the group) were 200x200, I set the spacing to -235 and the cards layered the opposite direction. I found I had to put them in the center of the space, but that worked out nicely for me and required no additional coding.
     
  12. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    (Hope my answer has not been pointed out yet... didn't read all the answers)

    You can add a Canvas component to the child image and "Override Sorting". This makes it possible to paint the child behind the parent. (EDIT: but then you probably need some more canvases to have everything drawn correctly... this is not so bad. it might even increase the performance)
     
  13. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    the 2d experimental build has some nice sorting features too

    https://docs.google.com/document/d/1fGYUBPJrJknCsYbAl4z3cOP8x6kealwjZfa2DwcXSgA/edit#

    Sorting Group

    The Sorting Group is a component that alters the order in which Renderers are rendered. Conventionally renderers in Unity will be sorted by several criteria such as Order in Layer or distance from camera. However, it was not possible to ensure that a group of renderers that share a common root be sorted together. Well, until now…


    Having renderers that belong to a common root render together is extremely useful. A common use case in 2D games are with complex characters.

     
  14. ahung89

    ahung89

    Joined:
    Feb 16, 2017
    Posts:
    2
    Yeah this is the cleanest and simplest way to do it. Word of warning though, don't put a Canvas on a Button or else your Button won't get click events anymore.
     
    gamedevgarage likes this.
  15. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    I've found that I had to add a graphic raycaster component along with any child canvas I've added in order for anything contained in that canvas to get click events. The same might hold true for the button (haven't tested this myself, though).
     
    Jacques_Botaki likes this.
  16. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    Also, watch out for a bug when using child canvases to render on top of their parent: https://issuetracker.unity3d.com/is...ot-display-correctly-according-the-sort-order

    To work around this issue, I had to force a sorting order refresh by temporarily changing it to something silly like -1000, then change it back to what it's supposed to be for the child to render on top of the parent.

    Code (csharp):
    1.  
    2.            // HACK - There's a bug in Canvas sort order where it doesn't appear to render correctly after becoming active from an inactive state.
    3.            // Touching the sort order value should fix this.
    4.            var editPanelCanvas = editPanelRect.GetComponent<Canvas>();
    5.            if (editPanelCanvas != null)
    6.            {
    7.                var temp = editPanelCanvas.sortingOrder;
    8.                editPanelCanvas.sortingOrder = -1000;
    9.                editPanelCanvas.sortingOrder = temp;
    10.            }
    11.