Search Unity

TextMesh Pro TMP_TextInfo.TMP_CharacterInfo[n] doesn't work for first few frames of scene in n >=8

Discussion in 'UGUI & TextMesh Pro' started by rubyleehs, Jun 25, 2018.

  1. rubyleehs

    rubyleehs

    Joined:
    Jun 24, 2018
    Posts:
    7
    TMP_TextInfo.TMP_CharacterInfo[n] doesn't work for first few frames of scene if n >=8

    It will throw an IndexOutOfRangeException if n>=8 but if you continue ignoring the error, it magically works


    Code (CSharp):
    1. namespace TMPro
    2. {
    3.     public class SomethingClass: MonoBehaviour
    4.     {
    5.     private TMP_Text TMPtextComponent;
    6.     public int index;
    7.  
    8.         Start()
    9.         {
    10.             TMPtextComponent = gameObject.GetComponent<TMP_Text>();
    11.         }
    12.         Update()
    13.         {
    14.             Debug.Log(TMPtextComponent.textInfo.characterInfo[index]);
    15.         }
    16.     }
    17. }
    Here is code to try it out. Just attach it to a TextMeshPro.

    So. How do I fix/get around this?
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Text objects are processed once per frame just before culling takes place in OnPreCull(). This is done to allow users to update / change the properties of the text object in all Update functions including LateUpdate(). As such, until the text object is processed, the content of the textInfo is reflective of whatever the previous time the object was re-generated / processed.

    Assuming that in your example we are looking at a newly created text object which contains the text "New text", the allocated characterInfo for that text object is only for 8 characters. So checking index = 8 would throw an OutOfRangeException.

    Now, I presume the information you really want is how can you get the correct characterInfo... So given the text object will be updated after you check the characterInfo in Update(), you need to force an update of the text object. This can be done by using the ForceMeshUodate() function.

    Code (csharp):
    1.  
    2. namespace TMPro
    3. {
    4.     public class SomethingClass:
    5.     {
    6.     private TMP_Text TMPtextComponent;
    7.     public int index;
    8.  
    9.     Start()
    10.         {
    11.             TMPtextComponent = gameObject.GetComponent<TMP_Text>();      
    12.             TMPtextComponent.text = "Some new text that contains more than 8 characters.";
    13.          
    14.              // We force the text object to be updated right away. By doing so, the content of the textInfo and characterInfo will have been updated for us to use in Update().
    15.             TMPtextComponent.ForceMeshUpdate();
    16.  
    17.         }
    18.  
    19.        Update()
    20.         {
    21.            Debug.Log(TMPtextComponent.textInfo.characterInfo[index]);
    22.         }
    23.     }
    24. }
    25.  
    Note that unless another property of the text object is changed after calling ForceMeshUpdate(), the text object will not be updated as it normally would since all properties have now been processed earlier.

    Also note the various arrays contains in textInfo are allocated in blocks and as such the size of an array like characterInfo is not representative of the number of characters. For that use textInfo.characterCount or whatever the relevant one is for what you are checking.
     
    FernandoHC and rubyleehs like this.
  3. rubyleehs

    rubyleehs

    Joined:
    Jun 24, 2018
    Posts:
    7
    adding TMPtextComponent.ForceMeshUpdate(); in my start worked. Thanks alot!

    To those reading, even if you aren't updating the text(like you changed it in scene view before hitting start), you would still need to add the above.
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Anytime you need to get the results of the layout / generation of the text before it is normally processed in OnPreCull(), you would use ForceMeshUpdate().

    A good example of this would be needing the bounds of a text object to adjust the size of the RectTransform or something.
     
  5. DearUnityPleaseAddSerializableDictionaries

    DearUnityPleaseAddSerializableDictionaries

    Joined:
    Sep 12, 2014
    Posts:
    135
    The issue with this is that the `tmpro.textInfo.characterInfo[index].bottomRight.x` is wrong for the first few frames, even with `ForceMeshUpdate()`.
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Once the text object has been processed which would occur when using ForceMeshUpdate() the positions should be correct.

    These positions are in object space so make sure you account for that by adding the appropriate offset based on their Transform / RectTransform positions.

    BTW: I would suggest adding a TMP_TextInfoDebugTool.cs script on the relevant text object and enabling show characters. This would allow you to visualize the bounds of each character to see if they are in the right place. This script is found in the TMP Examples & Extras.

    P.S. If you are using Layout Components, it is possible the layout system hasn't updated yet where when it does, it would affect the position and size of object which would alter the reported positions.