Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

UI Inverse Mask

Discussion in 'UGUI & TextMesh Pro' started by Ben-BearFish, Jul 20, 2015.

  1. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    Is it currently possible to create an inverse mask with Unity's UI mask component? That is hide everything that is under or overlapping the mask.
     
    awsapps likes this.
  2. William_Lee_Sims

    William_Lee_Sims

    Joined:
    Oct 11, 2014
    Posts:
    40
    Poking through the UI source code, I don't think this is possible. Mask.cs is an IMask, and MaskableGraphic.cs appears to uses the IMask interface to alter itself. Unfortunately, I can't find the path where the graphic from Mask is passed as the color mask graphic.

    This is a total guess (and hopefully somebody can find more information), but you'd have to make a new "InvertibleMask.cs". It would have an additional public boolean for "Inverted" (which triggers a NotifyMaskStateChanged). The "graphic" property could check the "Inverted" variable and return either an inverted graphic (harder) or an alternate graphic (easier).
     
  3. Senshi

    Senshi

    Joined:
    Oct 3, 2010
    Posts:
    557
  4. rat01

    rat01

    Joined:
    Jan 26, 2018
    Posts:
    1
    I don't know of a way to do this. The next best thing is to parent sprites to the UI camera, where you can use the Mask Interaction property on sprites, but that leaves you unable to interact with other true UI elements
     
  5. nicloay

    nicloay

    Joined:
    Jul 11, 2012
    Posts:
    540
    Very simple and straight forward implementation is to switch stencil operation on the masked object.

    Here is a simple script which must be places instead of Image (beneath the mask object)
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Rendering;
    4. using UnityEngine.UI;
    5.  
    6. public class InvertedMaskImage : Image {
    7.     public override Material materialForRendering
    8.     {
    9.         get
    10.         {
    11.             Material result = base.materialForRendering;
    12.             result.SetInt("_StencilComp", (int)CompareFunction.NotEqual);
    13.             return result;
    14.         }
    15.     }
    16. }
    17.  
    No any shader code or new materials.

    You can check full details and screenshots here http://answers.unity.com/answers/1492803/view.html
     
  6. dookeybre

    dookeybre

    Joined:
    Nov 10, 2012
    Posts:
    6
    Thanks a lot @nicloay . Now all my regular UI Images are broken. Do not add this script.
     
  7. InakiAndres

    InakiAndres

    Joined:
    Jan 9, 2019
    Posts:
    19
    LOL its true! all my uI images was broken after that!
     
  8. burakcatalok

    burakcatalok

    Joined:
    Feb 24, 2018
    Posts:
    2
    Images are broken
     
  9. GladStudio

    GladStudio

    Joined:
    Mar 7, 2016
    Posts:
    16
    My tests showed that if you create new material based on the base, the problem of breaking Images disappears.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.UI;
    4.  
    5. public class InvertedMaskImage : Image
    6. {
    7.     public override Material materialForRendering
    8.     {
    9.         get
    10.         {
    11.              Material result = new Material(base.materialForRendering);
    12.              result.SetInt("_StencilComp", (int)CompareFunction.NotEqual);
    13.              return result;
    14.  
    15.         }
    16.     }
    17. }
     
  10. Tochilo

    Tochilo

    Joined:
    Jan 16, 2016
    Posts:
    3
  11. UDN_5c806b49-d8a0-4f67-a296-c12c91aa7396

    UDN_5c806b49-d8a0-4f67-a296-c12c91aa7396

    Joined:
    Jan 9, 2017
    Posts:
    151
    Decided to give this a go. And... @nicloay code broke it all :eek:
     
  12. GladStudio

    GladStudio

    Joined:
    Mar 7, 2016
    Posts:
    16
    Maybe you're using it not correct way? Please show me how you use this component
     
  13. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    Dont use this code without prior saving, it destroyed everything and I could not recover!

    For my scenario, which was sufficient is the following: I wanted to create a "highlight" on certain areas of the screen for a tutorial, making everything else a bit darker. So I created a 512x512 50% transparency black image into which I cut a 64x64 hole. Now I scale this image to make everything darker but the center. Won't work for many scenarios but may be helpful for some :)
     
    Last edited: Jul 27, 2020
    Luis0413, maewionn and HunterAhlquist like this.
  14. TheFloatingSheep

    TheFloatingSheep

    Joined:
    Sep 27, 2014
    Posts:
    9
    Wish I would've scrolled this far before trying it.
    Guess there's gotta be a first time for everything.

    urgh
     
  15. TheFloatingSheep

    TheFloatingSheep

    Joined:
    Sep 27, 2014
    Posts:
    9
    If anyone can't fix their UI after closing and reopening, try a right click in Assets -> reimport all assets, it did it for me.

    Make sure to delete the faulty script.
     
  16. grobiann0

    grobiann0

    Joined:
    Feb 14, 2019
    Posts:
    1
    If you use the code above and the game is broken, use the code below to restore it.
    Just run it once in the game using the code below.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.UI;
    4.  
    5. public class InvertedMaskImage : Image {
    6.     public override Material materialForRendering
    7.     {
    8.         get
    9.         {
    10.             Material result = base.materialForRendering;
    11.             result.SetInt("_StencilComp", (int)CompareFunction.Always);
    12.             return result;
    13.         }
    14.     }
    15. }
     
  17. TheCoder5550

    TheCoder5550

    Joined:
    Sep 18, 2018
    Posts:
    1
    This code should work:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.UI;
    4.  
    5. public class InvertedMaskImage : Image {
    6.     public override Material materialForRendering
    7.     {
    8.         get
    9.         {
    10.             Material result = Instantiate(base.materialForRendering);
    11.             result.SetInt("_StencilComp", (int) CompareFunction.NotEqual);
    12.             return result;
    13.         }
    14.     }
    15. }
    Original answer:
    http://answers.unity.com/answers/1492803/view.html
     
    Pecek, mitaywalle, dpomza and 2 others like this.
  18. JavoCL

    JavoCL

    Joined:
    May 23, 2017
    Posts:
    1
    Hello everyone
    I found this on Youtube. I tested it in 2019.4.17, 2020.1.2 and 2020.2.1, and it works,with UI totally fine

     
  19. WidmerNoel

    WidmerNoel

    Joined:
    Jun 3, 2014
    Posts:
    64
    @JavoCL 's video recommendation works for me. Looks like the proper way of doing inverse UI masks :)
     
    zacharyaghaizu likes this.
  20. awsapps

    awsapps

    Joined:
    Jun 15, 2021
    Posts:
    74
    Is there a way to create an Inverse MASK instead of inverting the image masked itself?
     
    Last edited: Oct 2, 2021
    ericshearerL3Harris and Avalin like this.
  21. Hemaolle

    Hemaolle

    Joined:
    Jun 25, 2013
    Posts:
    10
    Check out the solution in this repo: https://github.com/mob-sakai/UnmaskForUGUI. The solution there supports also doing multiple inverse masks instead of just one, which seemed to be a limitation for me with other solutions shared before.
     
    lukas_dinox, Emanx140 and angelonit like this.
  22. Cloud-Yo

    Cloud-Yo

    Joined:
    Sep 5, 2014
    Posts:
    12
    I got to the script in this thread, used it and it worked as expected. What happened was that it overrides the Image component which you have to delete off of your Masked UI Image since it creates a conflict. I then plugged in the original sprite into the script's exposed fields and that's it. worked as expected.
    The orange circle is the UI Image with the mask Component attached. here Im just toggling the Show Mask Graphic check box.
    The masked item is a child of the orange circle.

    Hope this helps.
    aXlgRaHvxS.gif

    upload_2022-1-2_22-26-57.png
     
  23. angelonit

    angelonit

    Joined:
    Mar 5, 2013
    Posts:
    40
    THIS with the tutorial here solved it for me
     
  24. EJSainz

    EJSainz

    Joined:
    Mar 24, 2015
    Posts:
    29
    _geo__ likes this.
  25. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,247
    Thanks for the maskable comment. Missed to add that. If you look at the ui source then you will see the actual stencil value will be calculated based on the number of maskable components in the parent if maskable = true and forced to 0 otherwise (see the call to MaskUtilities.GetStencilDepth()). Turning it off makes it work but you also loose the ability to stack multiple of them.

    I'll add the original response here as unity answers might not be around forever.
    0701182B-4A2F-4E18-9778-6B4AFF5208E0.jpeg

    If you wonder about the meaning of the stencil values (3 and 6) here are the enums which they are coming from:
    Stencil Compare
    Stencil Operation
     
    Last edited: Aug 8, 2022
  26. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    This is working. It's important to get a copy of the base material and to now work directly on it, or you'll mess it all up.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.Rendering;
    6.  
    7. public class CutoutMaskUI : Image
    8. {
    9.     public override Material materialForRendering
    10.     {
    11.         get
    12.         {
    13.             // get a copy of the base material or you going to F*** up the whole project
    14.             Material material = new Material(base.materialForRendering);
    15.             material.SetInt("_StencilComp", (int)CompareFunction.NotEqual);
    16.             return material;
    17.         }
    18.     }
    19. }
     
    styrbo, triangle4studios and _geo__ like this.
  27. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,247
  28. triangle4studios

    triangle4studios

    Joined:
    Jun 28, 2020
    Posts:
    33

    If you did f- up your whole project, just change the compare function to (int)CompareFunction.Equal and then remove it, and add the code that AndreaMar Posted.
     
    AndreaMar likes this.
  29. styrbo

    styrbo

    Joined:
    Sep 25, 2018
    Posts:
    4
    in unity 2021.3.6f1 UnityEngine.UI was updated, so if image maskable value not updated then
    stencil value is not update.

    this is help to me:
    Code (CSharp):
    1.  
    2. protected override void Start()
    3. {
    4.     base.Start();
    5.     StartCoroutine(Fix());
    6. }
    7.  
    8. private IEnumerator Fix()
    9. {
    10.     yield return null;
    11.     maskable = !maskable;
    12. }
    13.  
    14.  
     
    ShelbyJuno likes this.
  30. bruno_agonalea

    bruno_agonalea

    Joined:
    Aug 24, 2023
    Posts:
    1
    I'm using the same CutoutMaskUI for screen transitions, and when I started using async scene loading, CutoutMaskUI just didn't render anything. I applied @styrbo's suggestion, but it didn't work either; so I tried instead to turn maskable off and on. And it worked!
    I'm using Unity 2022.3.10f1.
    So this is what I added to the original CutoutMaskUI to make it work in async loading scenes:

    Code (CSharp):
    1. protected override void Start()
    2. {
    3.     base.Start();
    4.     StartCoroutine(Fix());
    5. }
    6.  
    7. /// Fix for async loading scenes
    8. private IEnumerator Fix()
    9. {
    10.     yield return null;
    11.     maskable = false;
    12.     maskable = true;
    13. }
     
    Dragontech97 likes this.