Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Mask with nested Canvas with override sorting

Discussion in 'UGUI & TextMesh Pro' started by dvillaverde-drakhar, Nov 26, 2019.

  1. dvillaverde-drakhar

    dvillaverde-drakhar

    Joined:
    Aug 29, 2018
    Posts:
    11
    Hi,

    I have a Unity Mask trying to mask all their children. The problem is that some of his children need to change their sorting order. To do that I've used a Canvas with override sorting. Every children using this kind of Canvas is not masked, but if override sorting is disabled, it is masked.

    How can I correctly mask these images while keeping the sorting order that I want?

    Thanks for reading :).
     
    Last edited: Dec 9, 2019
    AlejMC likes this.
  2. dvillaverde-drakhar

    dvillaverde-drakhar

    Joined:
    Aug 29, 2018
    Posts:
    11
  3. kamarainen

    kamarainen

    Joined:
    Dec 17, 2016
    Posts:
    2
    I want to know that as well.
     
  4. Berno

    Berno

    Joined:
    Oct 29, 2014
    Posts:
    40
    This is a problem for me now too.
    I think we need to raise a bug?
     
    AlejMC likes this.
  5. Berno

    Berno

    Joined:
    Oct 29, 2014
    Posts:
    40
    I have it working now.
    Add the child object with a Canvas that overrides sorting.
    Add the mask and image you need to that object or child making sure the objects to be masked out are children of it.
     
  6. neokit

    neokit

    Joined:
    May 28, 2016
    Posts:
    6
    Can you explain more? I don't know Am I stupid or your comment is too complicated.
     
  7. Berno

    Berno

    Joined:
    Oct 29, 2014
    Posts:
    40
    In the hierarchy we have a Canvas at the top.
    We need to add a child Canvas under that to override the sorting order.
    In order for a mask to work correctly in this situation it needs to be a child of this second Canvas in the hierarchy.
     
    AlejMC likes this.
  8. better_walk_away

    better_walk_away

    Joined:
    Jul 12, 2016
    Posts:
    291
    What if we want to create a scroll view with RectMask2D, and its children have the Canvas component attached to them? We need the nested Canvas because we want to control the sorting layer of the children, so we have to use the Override Sorting to achieve this. But once enabling the Override Sorting option, the RectMask2D that is under the parent Canvas no longer works, it is not masking the children that have the nested Canvas with Override Sorting enabled.
    How do we address this issue?
     
  9. zuu1996

    zuu1996

    Joined:
    Jul 14, 2020
    Posts:
    1
    Use CanvasRenderer.EnableRectClipping(Rect rect) instead
     
    devsfh likes this.
  10. subramaniyanvgatmoback

    subramaniyanvgatmoback

    Joined:
    Oct 13, 2016
    Posts:
    15
    I am also facing this issue can someone help me out. Can someone tell how to raise the bug to unity.
     
  11. visca_c

    visca_c

    Joined:
    Apr 7, 2014
    Posts:
    30
    I recently ran into the same problem. Here is a script I wrote to work around it.
    Attach it to the child game object, and set the target to the mask RectTransform game object.
     

    Attached Files:

    coderEFE and xaldin-76 like this.
  12. xaldin-76

    xaldin-76

    Joined:
    Oct 1, 2016
    Posts:
    25
    This works but doesn't take the parent rotation into account. Is it possible to do that with a rect even?

    Edit: I'm done, never going to use Images again for any layers, just sprite renderers for now as they have sprite masks
     
    Last edited: Jul 6, 2021
    OrderOfOut likes this.
  13. OrderOfOut

    OrderOfOut

    Joined:
    Sep 21, 2018
    Posts:
    37
    Has this been fixed yet? We have the same issue and it's holding up a client project significantly. We are also pro users, so we would appreciate if a Unity rep would get on this. Broken mask functionality is very much not expected behavior for a professional development environment.

    Also, some more blunt engineer advice: Throw out the UI Canvas system. It's abysmal. Integrate a new system into the regular 2D world space with the same laws of physics - sprite renderers, sorting orders, etc. -- and keep it there.
     
    Last edited: Mar 2, 2022
    xaldin-76 likes this.
  14. coderEFE

    coderEFE

    Joined:
    Dec 8, 2019
    Posts:
    1
    I had the same issue, but this script helped mask the child object that had a Canvas component. Thanks for the help!
     
  15. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    453
    Thank you, great script, solves my problem.

    I have a ScrollRect and some objects under it that override sort order in separate Canvas elements, in order to be able to drag them and render the dragged object over the rest. This had the effect that on the edges of the ScrollRect the Images with overridden sort orders don't respect the ScrollRect Viewport mask anymore and are rendered outside the ScrollRect. However, the script solves this. Much obliged.
     
  16. AirnamicsJaniZ

    AirnamicsJaniZ

    Joined:
    May 28, 2021
    Posts:
    6
    For me the script works even without Update calls. I just call SetTargetClippingRect at start and that's it. Tried it with dragging around ScrollRect, dragging object inside ScrollRect, scaling and resizing ScrollRect - all seem to work. My mask is RectMask2D (not the default Mask that you're given). Not sure if there's any situation where clipping stops working.

    Shoutout to @visca_c for the original script, here's my implementation:
    Code (CSharp):
    1. private void SetTargetClippingRect(RectTransform maskRectTransform, CanvasRenderer canvasRendererToClip)
    2. {
    3.     Rect rect = maskRectTransform.rect;
    4.     Vector2 offset = maskRectTransform.localPosition;
    5.     Transform parent = maskRectTransform.parent;
    6.     Canvas canvas = parent.GetComponent<Canvas>();
    7.     while( canvas == null || !canvas.isRootCanvas )
    8.     {
    9.         offset += (Vector2)parent.localPosition;
    10.         parent = parent.parent;
    11.         canvas = parent.GetComponent<Canvas>();
    12.     }
    13.     rect.x += offset.x;
    14.     rect.y += offset.y;
    15.     canvasRendererToClip.EnableRectClipping( rect );
    16. }
    Edit: Ok so I found out that it does fail when you change to certain resolutions. So looks like it needs to be reset then.
     
    Last edited: Dec 14, 2022
  17. AirnamicsJaniZ

    AirnamicsJaniZ

    Joined:
    May 28, 2021
    Posts:
    6
    My final working implementation, beyond this I leave it to the community to improve and hopefully for Unity to implement their own solution.

    Put this near object you want clipped, remember to reference CanvasRenderer in Editor and have some manager class call Initialize with root (top-most) canvas and rect transform of object that holds mask.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class MaskedObject : UIBehaviour
    5. {
    6.     [SerializeField]
    7.     private CanvasRenderer canvasRendererToClip = null;
    8.  
    9.     private Canvas rootCanvas = null;
    10.     private RectTransform maskRectTransform = null;
    11.     private bool initialized = false;
    12.  
    13.     protected override void OnRectTransformDimensionsChange()
    14.     {
    15.         base.OnRectTransformDimensionsChange();
    16.         if( initialized )
    17.         {
    18.             SetTargetClippingRect();
    19.         }
    20.     }
    21.  
    22.     public void Initialize(Canvas rootCanvas, RectTransform maskRectTransform)
    23.     {
    24.         this.rootCanvas = rootCanvas;
    25.         this.maskRectTransform = maskRectTransform;
    26.         SetTargetClippingRect();
    27.         initialized = true;
    28.     }
    29.  
    30.     private void SetTargetClippingRect()
    31.     {
    32.         Rect rect = maskRectTransform.rect;
    33.         // Get local position of maskRect as if it was direct child of root canvas, then offset mask rect by that amount
    34.         rect.center += (Vector2)rootCanvas.transform.InverseTransformPoint( maskRectTransform.position );
    35.         canvasRendererToClip.EnableRectClipping( rect );
    36.     }
    37. }
     
    Last edited: Dec 15, 2022
    RyanC_Joy and Rugbug_Redfern like this.
  18. Deleted User

    Deleted User

    Guest

    Yeah, this is still broken. The solution posted here is cumbersome and this should just work out of the box. Unity, is there any update on the status of this? Regardless of sort order, the object is a child of a Rect2DMask, so if it's outside of the clipping bounds it should be masked. That's what everyone here expects, and it doesn't work.

    Currently I've managed a simple solution for where I need this, by making each object sit on the same level in the hierarchy. So:
    [Parent with RectMask2D]
    ----[Child1]
    ----[Child2] < -- child 2 is drawn above child 1.
    ---- etc etc.

    I'm not sure it's supposed to work THIS way either, but right now it's working. By the way I have no canvases on any of these objects. The parent of the rectmask2d object has a Canvas with general sort order set.
     
    AlejMC likes this.
  19. AlejMC

    AlejMC

    Joined:
    Oct 15, 2013
    Posts:
    149
    I think this was never addressed nor fixed, currently on this same situation, needed a 'specific' sorting, in which objects still draw after the masker one yet somehow the stencil value is either not set nor handled. Same with RectMask2D components.
    Going to try maybe those hand made 'EnableClippingRect' but sounds totally overkill.