Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Overdraw: SpriteRenderer in UI?

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

  1. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    612
    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:
    612
    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:
    385
    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
    glenneroo and hungrybelome like this.
  5. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    612
    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?)