Search Unity

Overdraw: SpriteRenderer in UI?

Discussion in 'UGUI & TextMesh Pro' started by Marrt, Jul 10, 2015.

  1. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Question:
    Should i start writing UI-scaling-functions for the SpriteRenderer or will the Unity-UI get an update to support non-quad geometry at some point in the future?

    Background:
    I want to use big circles as analog-pad indicators. Obviously this causes overdraw because the UI-System creates only quads (i can see the fps drop on the device while those circle images are active).

    The solution would be to have a mesh-geometry similar to the SpriteRenderer, that cuts out most transparent areas. You can actually use a SpriteRenderer in the UI but there are some restrictions:
    • SpriteRenderer will not be visible in ScreenSpace-Overlay mode, you need to use ScreenSpace-Camera
    • SpriteRenderer completely ignores the RectTransform except for centering in it, so scaling has to be done by yourself, but that can easily be done with some functions
    • At least one additional Drawcall if i use both normal GUI-Image and SpriteRenderers within the UI - but that is much less of a performance hit than the fillrate

    Picture:
    left circle: UI-Image, right circle: SpriteRenderer
    left side: Shaded Wirframe, right side: Overdraw
    upload_2015-7-10_11-57-58.png

    Thank you for reading up to this point.
     
  2. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    I ended up writing a costum scaling function for my UI-Spriterenderers. Now they look exactly like the previous UI-Images. Scaling function is simple:

    Code (CSharp):
    1.  
    2. public static SpriteRenderer AddScaledUISpriteRenderer(RectTransform rectTrans, int? sortingOrder = null){
    3.         //Adds a child GameObject to the UI-GameObject that copies the sprite of the image
    4.         //Needs to be a child because we need to scale it but still anchor its position to the old Object
    5.         //If we would add a SpriteRenderer to the actual Image-Object and scale it accordingly, children of it will be scaled too.
    6.        
    7.         float pxWidth        = rectTrans.rect.width;            //width  of the scaled UI-Object in pixel
    8.         float pxHeight        = rectTrans.rect.height;        //height of the scaled UI-Object in pixel
    9.        
    10.         Image uiImage        = rectTrans.GetComponent<Image>();
    11.        
    12.         float spriteSizeX    = uiImage.sprite.bounds.size.x;    //width  of the unscaled sprite in pixel
    13.         float spriteSizeY    = uiImage.sprite.bounds.size.y;    //height of the unscaled sprite in pixel
    14.        
    15.         //scale that needs to be applied to the SpriteRender-Gameobject to give it the same screen size as the UI-Image
    16.         float scaleX = pxWidth    /spriteSizeX;
    17.         float scaleY = pxHeight    /spriteSizeY;
    18.        
    19.        
    20.         //create new SpriteGameObject in UI
    21.         GameObject spriteGO        = new GameObject();
    22.         spriteGO.name            = rectTrans.name+"Sprite";                    //give it a name
    23.         spriteGO.layer            = LayerMask.NameToLayer("UI");                //culling layer, if needed
    24.         spriteGO.transform.localScale = new Vector3(scaleX, scaleY, 1F);    //scale the SpriteRenderer
    25.        
    26.         RectTransform sRect        = spriteGO.AddComponent<RectTransform>();    //add a rect Transform to replace the normal Transform
    27.         sRect.SetParent(rectTrans, false);                                    //make the image Object the new parent, so this will move with it      
    28.        
    29.         SpriteRenderer spriteR    = spriteGO.AddComponent<SpriteRenderer>();    //Add a sprite Renderer to the new Object
    30.         spriteR.sprite            = uiImage.sprite;                            //copy the Image's sprite
    31.         spriteR.color            = uiImage.color;                            //copy the Image's coloration
    32.         spriteR.enabled            = uiImage.enabled;                            //copy enabled state
    33.         spriteR.sortingOrder    = sortingOrder ?? 1;                        //needed to be at least 1 if the UI is drawn above a render texture
    34.                
    35.         uiImage.enabled            = false;    //disable the old UI-Image
    36.                        
    37.         return spriteR;
    38.     }
    39.  
    Previous overdraw:
    upload_2015-7-13_22-8-1.png
    New overdraw:
    upload_2015-7-13_22-8-28.png
     
    Last edited: Jul 13, 2015
  3. Miguel-Ferreira

    Miguel-Ferreira

    Joined:
    May 8, 2015
    Posts:
    90
    +1, this is one reason why UI is currently so slow, the overdraw is super heavy, specially on Android devices
     
  4. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    I would like to contribute.
    As long as we keep the sprite or even a Mesh quad parented to some parent-rect, we can stretch that parent rect and sprite will follow it.

    Just make sure to attach the following script to the child sprite (or child mesh quad)


    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. // Attach this script onto a sprite renderer.
    5. // Stretches a sprite to follow corners of parent rect.
    6. // Make sure it's parented to a "dummy" rectTransform and stretch that instead of our tranform.
    7. //
    8. // IgorAherne March 2017
    9. // Marrt  July 2015
    10. // https://forum.unity3d.com/threads/overdraw-spriterenderer-in-ui.339912/#post-3009616
    11. // https://forum.unity3d.com/threads/overdraw-spriterenderer-in-ui.339912/#post-3009616
    12.  
    13. [ExecuteInEditMode, RequireComponent(typeof(RectTransform))]
    14. public class QuadScalerRelativeToAnchors : MonoBehaviour {
    15.  
    16.  
    17.     [SerializeField, HideInInspector] RectTransform _myRectTransform;
    18.     [SerializeField, HideInInspector] RectTransform _parentRectTransf;
    19.     [SerializeField, HideInInspector] SpriteRenderer _mySpriteRenderer;
    20.  
    21.     void Reset() {
    22.         Start();
    23.     }
    24.  
    25.  
    26.     void Start() {
    27.         _parentRectTransf = transform.parent as RectTransform;
    28.         _mySpriteRenderer = GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
    29.         _myRectTransform = transform as RectTransform;
    30.     }
    31.  
    32.  
    33.  
    34.     void Update() {
    35.  
    36. #if UNITY_EDITOR
    37.         //if we are in execute in edit mode, in editor, we might not have some references yet.
    38.         //so establish them if needed:
    39.         if(_mySpriteRenderer == null  ||  _parentRectTransf == null  || _myRectTransform == null) {
    40.             Start();
    41.         }
    42. #endif
    43.  
    44.         keepScaleRelativeToParent();
    45.     }
    46.  
    47.  
    48.  
    49.     public void keepScaleRelativeToParent() {
    50.  
    51.         float pxWidth = _parentRectTransf.rect.width;            //width  of the scaled UI-Object in pixel
    52.         float pxHeight = _parentRectTransf.rect.height;        //height of the scaled UI-Object in pixel
    53.  
    54.         if (float.IsNaN(pxHeight)  ||  float.IsNaN(pxWidth)) {
    55.             //unity hasn't not yet initialized (usually happens during start of the game)
    56.             return;
    57.         }
    58.  
    59.         float spriteSizeX = 1;
    60.         float spriteSizeY = 1;
    61.  
    62.         if (_mySpriteRenderer) {
    63.             spriteSizeX = _mySpriteRenderer.sprite.bounds.size.x;  //width  of the unscaled sprite in pixel
    64.             spriteSizeY = _mySpriteRenderer.sprite.bounds.size.y;  //height of the unscaled sprite in pixel
    65.         }
    66.  
    67.         //scale that needs to be applied to the SpriteRender-Gameobject to give it the same screen size as the UI-Image
    68.         float scaleX = pxWidth / spriteSizeX;
    69.         float scaleY = pxHeight / spriteSizeY;
    70.  
    71.        
    72.         //create new SpriteGameObject in UI
    73.         this.gameObject.layer = LayerMask.NameToLayer("UI");            //culling layer, if needed
    74.         _myRectTransform.localScale = new Vector3(scaleX, scaleY, 1F);    //scale our SpriteRenderer
    75.  
    76.  
    77.         //position self in the middle of parent
    78.         _myRectTransform.anchoredPosition3D = new Vector3(0,0, _myRectTransform.anchoredPosition3D.z);
    79.         _myRectTransform.anchorMin = Vector2.one * 0.5f;
    80.         _myRectTransform.anchorMax = Vector2.one * 0.5f;
    81.  
    82.         float myLocalScale_x = Mathf.Max(0.0001f, _myRectTransform.localScale.x);
    83.         float myLocalScale_y = Mathf.Max(0.0001f, _myRectTransform.localScale.y);
    84.  
    85.         _myRectTransform.sizeDelta = new Vector2( Mathf.Max(0.0001f, _parentRectTransf.rect.width / myLocalScale_x),
    86.                                                   Mathf.Max(0.0001f, _parentRectTransf.rect.height / myLocalScale_y) );
    87.     }
    88.  
    89. }
    90.  
    91.  
     
    Last edited: Mar 30, 2017
    ozantekce, glenneroo and hungrybelome like this.
  5. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
    Since people still like this thread:
    This workaround is not needed anymore, unity now supports this natively in UI-Sprites
     
    IgorAherne likes this.
  6. Marc-Zaku

    Marc-Zaku

    Joined:
    Jan 30, 2015
    Posts:
    2
    @Marrt Could you elaborate please, through which component and in which Unity Versions UI-Sprites are supported natively now?

    Edit: Nevermind:
    https://docs.unity3d.com/2018.3/Documentation/ScriptReference/UI.Image.html
    the bool useSpriteMesh has the feature that we need.
    The feature exists in Unity 2018.4 and upwards.
    Hint: you have to assign as sprite in order to set the field in the inspector, or you need to switch the inspector to debug mode.
     
    Last edited: May 6, 2020
    CodeRonnie and Marrt like this.
  7. awsapps

    awsapps

    Joined:
    Jun 15, 2021
    Posts:
    74
    So UI-Sprites are preferred over Sprite Renderers ? (Just for sake of re-usability in both Canvas and GameWorld?)
     
  8. ozantekce

    ozantekce

    Joined:
    Sep 23, 2021
    Posts:
    3
    This code was very useful for me, thank you for sharing.
     
    Last edited: Mar 15, 2024
    IgorAherne likes this.