Search Unity

Bug Material Tint has no Effect in Scrollview / On Maskable Image

Discussion in 'UGUI & TextMesh Pro' started by DragonCoder, Nov 2, 2020.

  1. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,698
    Hello community,

    the two rectangles in this screenshot have the same material with UiDefault shader and a green "tint" attached to them:
    Bug.PNG

    Only difference is that one is child of a scrollview and the other isn't.
    If I disable and re-enable the one in the scrollview it does update its color.
    It also updates correctly if I remove the "maskable" flag from the flat rectangle, but obviously that makes the scrollview useless.

    Is this desired behavior or a bug?
    In every case it is not allowing me to have scrollviews in menus that are meant to fade in and out smoothly.
    Is there a way to circumvent this?

    This happens in all recent versions I've tried. For example 2020.1.11f1.

    Have tried to add Canvas.ForceUpdateCanvases(); in an update function, but without effect.

    Here a copy of this very simple project:
    https://www.dropbox.com/s/xd0pokjuncdl3g1/ScrollViewBugTest.zip?dl=0

    Huge thanks for suggestions :)
     
  2. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,698
    Have found some more information here: https://forum.unity.com/threads/masked-material-shader-changes-not-working-correctly.521637/
    So apparently to enable masks, Unity duplicates and modifies the material of your object.
    So the material is not actually the same you assigned, once at runtime...
    However the function RecalculateMasking() causes the cloning again, with the new values.

    Therefore I have found a workaround. Add this class to the viewport object of the scrollable:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class SimpleMaskUpdater : MonoBehaviour
    7. {
    8.     static public bool do_update = false;
    9.     static public bool last_update = true;
    10.  
    11.     IMaskable[] maskables = null;
    12.     void OnEnable()
    13.     {
    14.         maskables = GetComponentsInChildren<IMaskable>();
    15.     }
    16.  
    17.     void Update()
    18.     {
    19.         if (do_update)
    20.         {
    21.             if (!last_update)
    22.                 maskables = GetComponentsInChildren<IMaskable>();
    23.             foreach (IMaskable msk in maskables)
    24.                 msk.RecalculateMasking();
    25.         }
    26.     }
    27.     void LateUpdate()
    28.     {
    29.         last_update = do_update;
    30.     }
    31. }

    Then just set SimpleMaskUpdater.do_update to True when starting to change the material values and to false when done with it.
    You might be able to ommit the "maskables = GetComponentsInChildren<IMaskable>();" inside the update function as well as the whole "last_update" variable for performance reasons. I need it in my project though because the elements can change frequently.

    Not very sure about the performance impact of RecalculateMasking(). It has not been significant in my case though and I only need it for GUI. If you want to use it all the time in a game, you might want a different workaround.