Search Unity

Does setting a TMP's text value to its current string cause the UI panel to be re-drawn?

Discussion in 'UGUI & TextMesh Pro' started by travlake, Jun 23, 2020.

  1. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    Assumptions on which my question is premised: My understanding is that whenever you update a UI element, for example setting a TextMeshPro's text to a new string, it dirties the entire UI panel and causes it to re-draw on that frame. Doing this on every frame for every panel in the UI is really slow, so we want to avoid dirtying panels that don't change. Correct me if any of this is wrong.

    Question: does the following code dirty the UI panel every frame or only first time the text is set to Hello?

    Code (CSharp):
    1. public TextMeshProUGUI testTmp; // imagine this is serialized with a reference to a TMP in the inspector
    2.  
    3. private void Update()
    4. {
    5.       testTmp.text = "Hello" ;
    6. }
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Any change to a child object of the Canvas where this child is either a Graphic element or Layout component will trigger are rebuild / re-layout of the entire Canvas. Depending on the complexity and number of child objects to a Canvas, this can indeed have significant overhead.

    In my opinion, it is best to group static objects (those that will not change) under their own canvas and those objects that will change under another. The idea is to minimize the number of objects that will have to be rebuilt / re-layout.

    In terms of the above example, this would trigger an initial rebuild as the text goes from null to "Hello". However, on subsequent updates, it would not because the text hasn't changed.

    I would recommend making sure that you only change the text when it needs to and not in an update loop which runs every frame.
     
  3. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    Awesome, thanks for the quick & helpful reply.

    I'm a bit confused about the best practices here. Imagine I have a UI element showing a character's frequently-changing status (hp, ammo, armor, speed, etc). There is a long and evolving list things that potentially affect this status (e.g. items, attacks from monsters, etc). What's the best practice for scripting this UI element?

    I'm currently using Update() method that checks every frame whether each status value has changed and, only if it has changed, reset the text. Based on what you said, this would be equivalent to just setting the text value every frame because if hasn't changed there wouldn't be a rebuild. This is what I've been doing but seems to go against your recommendation.

    What's wrong with this?

    Is the preferred alternative a component on the character itself that raises a CharacterStatusChangeEvent every time another event leads to a status change? The UI component would then only need to listen to CharacterStatusChangeEvent and update on that frame.
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Let's assume that TMP did not internally check if the text changed. This means that every single frame you would be setting this value. As such, always assume that changing a value triggers some potential overhead.

    Updating the text only when it needs to change is the better alternative.

    Having some script on the character that changes the text only when needed is the preferred method. Whether you are using some event or other implementation is fine as in either case you are not constantly changing the text.

    There are cases where changing the text every frame would make sense like a frame counter but when the text value may not need to be updated for several frames, it is best to avoid setting the text to the same value.
     
  5. travlake

    travlake

    Joined:
    Oct 4, 2019
    Posts:
    50
    You're right, it would make no sense for TMP not to check internally whether .text has changed every frame.

    For those of you arriving via Google in the future: the same does not appear to be true if you use .SetText() to handle formatting. The end of the SetText() method has

    Code (CSharp):
    1. SetVerticesDirty();
    2. SetLayoutDirty();
    so it will cause a re-draw even if nothing has changed. (Stephan can of course correct me if I'm wrong on this). That's in addition to it parsing the string, so definitely don't call SetText unless things have changed.