Search Unity

TextMesh Pro ForceMeshUpdate(true) not working when object inactive

Discussion in 'UGUI & TextMesh Pro' started by Trisibo, Apr 1, 2018.

  1. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    I'm trying to assign a text to an inactive TextMesh Pro text object, and then get the text info with
    textInfo
    . Before accessing
    textInfo
    , I call
    ForceMeshUpdate(true)
    to update the object; I would expect setting the parameter
    ignoreActiveState
    to
    true
    would update the object even if it's inactive, but it doesn't. Am I misinterpreting the parameter, or is it a bug?:

    Code (CSharp):
    1. textComponent.text = text;
    2. textComponent.ForceMeshUpdate(true);
    3. numCharsFromTextInfo = textComponent.textInfo.characterCount; // Returns incorrect data
    For now it seems activating the object just before updating and deactivating it afterward works, but it's not very nice, and doesn't work if one of the parent objects is inactive.
     
    Last edited: Apr 1, 2018
    ilmario and Rioneer like this.
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Thank you for taking the time to provide feedback.

    I'll take a look and make the necessary changes if required.
     
  3. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    Thanks. By the way, I may have found a pair of other things that look like bugs, should I report them from Unity's bug report tool?
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    It is up to you.

    If these are trivial and easily reproducible, you can report them here. If they are more challenging to reproduce, then submitting a bug report with Repro case is always best as I can focus on a solution as opposed to trying to reproduce something.
     
  5. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    I have addressed the incorrect behavior of the ForceMeshUpdate(bool ignoreActiveState). This change will be included in the next release of TMP.
     
  6. shell123

    shell123

    Joined:
    May 26, 2016
    Posts:
    4
    Was this fixed? I am still running into it. When the gameobject is inactive at the time of a ForceMeshUpdate(true) call the bounds are not updated. When the gameobject is active at the time of a ForceMeshUpdate(true) call the bounds are updated correctly.
     
  7. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    What version of TMP are you using?

    This should be working but once you let me know what version you are using, I can double check on my end.
     
  8. shell123

    shell123

    Joined:
    May 26, 2016
    Posts:
    4
    I am using 1.3.0.
     
  9. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    I'm seeing this behaviour in 1.2.4.

    I instantiate a prefab with a TextMeshProUGUI component and parent it to another game object. activeSelf is true, but activeInHierarchy is false. I set the text field, then call ForceMeshUpdate(true) but the preferredHeight is not updated and the textInfo field still has the character count of the original text on the prefab. Using Unity 2018.2.12f1. I've also tried calling SetAllDirty but no dice.

    edit: I updated to 1.3.0 but still no luck!

    Thanks,
    Sam
     
  10. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Let me take a look at this again. I should be able to get you an update tomorrow.
     
  11. WF_Bart

    WF_Bart

    Joined:
    Apr 16, 2014
    Posts:
    29
    Any update on this? I'm running into the same problem (also on 1.3.0)
     
  12. jk15

    jk15

    Joined:
    Jun 23, 2011
    Posts:
    49
    Happening to me also.
     
  13. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    I did address this issue in the current version of the TMP package that I am working on which hasn't been released yet.

    The release that will include the change will be 1.4.2 for Unity 2018.3 and 2.0.2 for Unity 2019.1 or newer.

    I will try to lookup the specific changes and if they can be easily made in 1.3.0, will post those in the next few days.

    Please feel free to re-post in this thread, if I don't provide an update on this within the next few days.
     
  14. giawa

    giawa

    Joined:
    Dec 6, 2015
    Posts:
    15
    Seeing this in 1.3.0 in Unity 2018.2. Cant update my version of Unity at this time. Is there a work around?
     
  15. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    I would suggest taking a look at the revised ForceMeshUpdate() in version 1.4.1 (since the source code is included with packages) and making the relevant changes on your end.
     
  16. silentslack

    silentslack

    Joined:
    Apr 5, 2013
    Posts:
    395
    Hi had this same issue with the bounds not updating correctly so I invoke a method a frame later and that seems to fix things.
     
  17. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Experiencing this too on Unity 2018.4.5 and TMP 1.4.1.
     
  18. alpapan

    alpapan

    Joined:
    Aug 10, 2019
    Posts:
    3
    I confirmed the same issue exists for 2019.2 and TMP downloaded with the Package Manager (2.0.1). It took me a while to figure it out!

    If an object the script is attached to is a child of an inactive game object, then it will not work. It has to be a child of an active object. No errors printed out.
     
    Last edited: Sep 9, 2019
  19. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    hmm im suffering this too, I simply cannot get the text to update even when I set it either with SetText or .text, using unity 2020.10a.12
     
  20. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Make sure you try with the latest preview package of TMP which is version 3.0.0-preview.3 and let me know if the issue still occurs.
     
    radiantboy likes this.
  21. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    I have to admit it was own logic error, I was doing findComponentsInChildren and not actually making sure that it was finding the right element. I did not realise that other items I had used were buttons etc. Once I checked against the name it actually worked perfectly and I removed all the hacks I had applied and it's still fine. So for now I think it is fixed and I am guilty of throwing a red herring into the works :) Thank you very much for the reply though, I am so happy when unity guys reply, Karl Jones (@karl_jones) has been fantastic in particular, that guy needs a nomination for helping the masses. Happy new year.
     
  22. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    This thread confuses me. The parameter is called "ignoreInactive". So I would expect that setting it to true means "ignore this call if the object is inactive."

    Everyone above is saying that ignoreInactive=true means that it should not ignore inactive, which seems completely backwards to me, and @Stephan_B has not commented on this.

    In my testing it's not working either way, but I'd love some clarification on which way it's supposed to work.
     
  23. RudyTheDev

    RudyTheDev

    Joined:
    Nov 26, 2012
    Posts:
    5
    I was looking at the same thing and as far as I can tell, the parameter doesn't actually do anything (regardless that it's named "backwards") on 2.0.1 2019.3.4f1:

    Code (CSharp):
    1. public override void ForceMeshUpdate(bool ignoreInactive)
    2. {
    3.     m_havePropertiesChanged = true;
    4.     m_ignoreActiveState = true;
    5.     OnPreRenderCanvas();
    6. }
    7.  
    I guess I set my code to .ForceMeshUpdate(true); for now and hopefully m_ignoreActiveState or something will use the value in the future.
     
  24. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Please test this with the latest version 1.5.0-preview.8 as the following changes were made in Preview 1.

    - Revised the ForceMeshUpdate() function as follows: public void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTextReparsing = false).
     
  25. RudyTheDev

    RudyTheDev

    Joined:
    Nov 26, 2012
    Posts:
    5
    Sorry for the late reply.

    This functionality doesn't actually affect me, so I don't have a test scenario where it matters. (I use the method to force an update because I'm resizing UI elements after changing the text and I need to know the text size. But my elements get enabled before I update the text, so the flag didn't/doesn't matter.)

    In any case, I tried both 1.5.0 preview.8 and 2.1.0 preview.8 (not sure what the difference is) and it works the same as far as I can tell. I tried both (false, false), (true, true) and (true, false) and saw no difference, but then I didn't expect to see any. At least I can say that nothing broke.
     
  26. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    1.5.0 is for Unity 2018.4 and 2.1.0 for Unity 2019.x and 3.0.0 for Unity 2020.x.
     
  27. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Still having this problem on Unity 2019.2.21f1, TMPro package preview.8 - 2.1.0.
     
  28. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Can you provide simple steps to reproduce this behavior?
     
  29. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    No idea if this is helpful?

    Code (CSharp):
    1. public class Testtt : MonoBehaviour {
    2.     public TMPro.TextMeshProUGUI tmp;
    3.  
    4.     IEnumerator Start() {
    5.         Debug.Log(tmp.textBounds.size); // 1
    6.         tmp.text = "This is some text!";
    7.         Debug.Log(tmp.textBounds.size); // 2
    8.         tmp.ForceMeshUpdate(true);
    9.         Debug.Log(tmp.textBounds.size); // 3
    10.         tmp.gameObject.SetActive(true);
    11.         Debug.Log(tmp.textBounds.size); // 4
    12.         yield return null;
    13.         Debug.Log(tmp.textBounds.size); // 5
    14.     }
    15. }
    tmp.gameObject is set to inactive in the beginning. Only the fitfth Debug.Log() shows the correct value, all the others print "(-4294967000.0, -4294967000.0, 0.0)" for me. The 3rd and 4th already *should* print the correct size.
     
    Last edited: Apr 9, 2020
  30. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Is this somehow being worked on?
     
  31. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Have you tried if the behavior remains with the latest releases?
     
    ratking likes this.
  32. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Thanks, it works now.

    By the way, is there any reason TMPro would behave like this:



    Specifically, setting a text (the title with the dark blue background) with autosize works the first time, but after setting the same text a second time, it ignores the autosizing apparently. It just overflows the text. (Which I don't actually allow, it should be an ellipsis.)

    This drives me crazy, because there is no way to work around this, as it always behaves like that, even when waiting a few frames or setting it a third time, etc.
     
  33. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Are you using layout components? Perhaps the returned preferred values change.

    I would have to get a repro project to figure out exactly why this is happening.
     
  34. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    No, the hierarchy of this object doesn't use any Layout components.

    I hope I can create a repo soon. Right now it doesn't add the linebreak at the first time anymore, and I don't know why's that. The only case where it works is when I change the text manually.
     
  35. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Hopefully, you can get me some repro project on this. If submitting a bug report with the project makes it easier for you then please do that and once you get the email from Unity with the Case #, please post it here.
     
  36. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    I can give you access to the project's repository via bitbucket. Could you PM me with the e-mail address I should use to add you?
     
  37. Santifocus

    Santifocus

    Joined:
    Oct 13, 2018
    Posts:
    6
    I might be missreading but the OnPreRenderCanvas call is aborted of the object never called Awake before
    Code (CSharp):
    1. if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) return;
    In other words if you call ForceMeshUpdate(true) it wont work if that object was never active before. May i suggest adding a second bool "Ignore Never awake"?

    Technically you can just activate and deactivate the gameobject so awake gets called but thats just a codesmell tbh.
     
  38. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Awake is only called once in the life cycle of the object. If the text object has never been awaken, the above code will return.

    If the object has been awaken, it might be disabled and if so it will return unless ForceMeshUpdate is forcing the update.

    What is the scenario / case you are trying to handle?
     
  39. Santifocus

    Santifocus

    Joined:
    Oct 13, 2018
    Posts:
    6
    Yes that is what I was trying to say with my Post.
    I pointed it out because from my point of view the expected behaviour of using ForceMeshUpdate(true) on a object that was never awoken is that it would work anyway because its supposed to ignore the active state and therefore also that it was never enabled before.

    Furthermore being forced to call

    Code (CSharp):
    1. if (!gameObject.activeSelf)
    2. {
    3.     gameObject.SetActive(true);
    4.     gameObject.SetActive(false);
    5. }
    just so you can use ForceMeshUpdate(true) is pretty ugly in my opinion.

    In my case the scenario involves a RectTransform whose sizeDelta needs to be readjusted based on the text that is put in it.

    Code (CSharp):
    1.  
    2. descriptionDisplayText.text = targetOption.Description.EnumerableString();
    3. descriptionDisplayText.ForceMeshUpdate(true);
    4. descriptionDisplayRect.sizeDelta = new Vector2(descriptionDisplayRect.sizeDelta.x, descriptionDisplayText.textBounds.size.y + descriptionDisplayTextPadding);
    5.            
    6. //The first ForceMeshUpdate was used so we can find out the text bounds, now we need to update it again because its canvas got changed
    7. descriptionDisplayText.ForceMeshUpdate(true);
     
  40. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    Active state relates to OnEnable and OnDisable. If the object has never been awaken, it will be awaken but that only happens once in the life cycle of the object.

    Awake is required for initialization.

    Why not use GetPreferredValues() to calculate what the size of the RectTransform should be.
     
  41. Santifocus

    Santifocus

    Joined:
    Oct 13, 2018
    Posts:
    6
    Interesting, i did not hear of that function before. Sadly I dont seem to find any good documentation on it nor does Riders decompiled code help me much either.

    I dont see a overload that gives the possibility to set a constant widht where I get the prefered Height back, am I missunderstanding something?
     
  42. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    TMP_Text.GetPreferredValues()

    Code (csharp):
    1.  
    2. /// <summary>
    3. ///  Function to Calculate the Preferred Width and Height of the text object given a certain string and size of text container.
    4. /// </summary>
    5. /// <param name="text"></param>
    6. /// <returns></returns>
    7. public Vector2 GetPreferredValues(string text, float width, float height)
    8. [/code
     
  43. Santifocus

    Santifocus

    Joined:
    Oct 13, 2018
    Posts:
    6
    Ah from testing it I see my missunderstanding, what I thougth widht/height would mean is the that they would both be used as forced restrictions but it seems that the prefered width uses the input height and the prefered height uses the input widht. So I can call it with whatever height because I dont care about the prefered widht.

    Thanks you for this information cleans up my code a bit ^^
     
    Stephan_B likes this.
  44. Johnnemann

    Johnnemann

    Joined:
    Mar 20, 2012
    Posts:
    11
    Hi,

    I'm still seeing the behavior with ForceMeshUpdate(true, true) on 2019.4.18f1, with TMPro 2.1.4.

    I call SetText(), then ForceMeshUpdate() on an inactive TMProUGUI object, but the textInfo reports 0 characters.

    Is there something I'm missing?

    Thanks

    Edit: I think this is actually due to a weird initialization/instantiation order that's specific to my setup. I'll figure it out on my end. Thanks!
     
    Last edited: Feb 19, 2021
  45. dimzki

    dimzki

    Joined:
    Apr 17, 2018
    Posts:
    12
    Hello, I still get the same problem in 2020.3.19f1
    Im trying to update a battle log when the object is inactive.

    Code (CSharp):
    1. battleResultLog.GetComponent<TextMeshProUGUI>().text = battleLogText;
    2. battleResultLog.GetComponent<TextMeshProUGUI>().ForceMeshUpdate(true);
    then i tried to get the height value for updating the scrollview content size

    Code (CSharp):
    1. // option 1:
    2. Vector2 prefSize = battleResultLog.GetComponent<TextMeshProUGUI>().GetPreferredValues();
    3. float prefHeight = prefSize.y + 15;
    4.  
    5. // option 2:
    6. float prefHeight = battleResultLog.GetComponent<TextMeshProUGUI>().preferredHeight + 15;
    7.  
    8. DebugLog.text(prefHeight.ToString());
    both options somehow didn't give the correct value (the Y size should be around 14,600 but it was 8749.9 instead)

    Code (CSharp):
    1. // set battle log content size
    2. battleResultLog.parent.GetComponent<RectTransform>().SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, prefHeight);
    3. // set scrollValue to default
    4. battleResultLog.parent.GetComponent<RectTransform>().anchoredPosition = new Vector2(10, -prefHeight);
    oh and about the script execution order, im not sure if i changed TMP's order or not (probably not), it's currently at:
    1. TMPro.TextContainer at -110
    2. TMPro.TextMeshPro at -105
    3. TMPro.TextMeshProUGUI -100
    *edit: right now, my only solution is to set the text value when the TMP's object is set to active. i was using BOLT visual scripting before i switched to C#, there was no problem with changing the text value when the object is inactive in bolt, but bolt is too slow so i switched all my script to C# in the end
     
    Last edited: Dec 13, 2021
  46. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    What version of the TMP package are you using? The latest for 2020.3 is version 3.2.0-pre.2.
     
  47. dimzki

    dimzki

    Joined:
    Apr 17, 2018
    Posts:
    12
    sorry for the late replay, i just checked and for some reason, my TMP package is at 3.0.6 - April 22, 2021 and i cant seem to update it. it says it's the latest one
     
  48. lucbloom

    lucbloom

    Joined:
    Mar 27, 2020
    Posts:
    44
    This solution works by activating every parent in the tree up to the root, then restoring the active state of each.
    Code (CSharp):
    1.         public static int GetLineCount(this TMP_Text label)
    2.         {
    3.             var disableds = label.transform.EnumerateAllParents(true).Where(p => !p.gameObject.activeSelf).ToList();
    4.             disableds.ForEach(p => p.gameObject.SetActive(true));
    5.             label.ForceMeshUpdate(true);
    6.             if (label.textInfo == null)
    7.             {
    8.                 Console.Log($"Label {label.name.Quoted()} doesn't have a 'textInfo' object. This is usually because it has not been 'Awoken' yet. activeSelf: {label.gameObject.activeSelf} activeInHierarchy: {label.gameObject.activeInHierarchy}");
    9.                 // Try to salvage:
    10.                 return label.text.Empty() ? 0 : (label.text.Count('\n') + 1);
    11.             }
    12.             disableds.ForEach(p => p.gameObject.SetActive(false));
    13.             return label.textInfo.lineCount;
    14.         }
    Utility:
    Code (CSharp):
    1.         public static IEnumerable<Transform> EnumerateAllParents(this Transform go, bool includeSelf = false)
    2.         {
    3.             if (!includeSelf)
    4.             {
    5.                 go = go?.parent;
    6.             }
    7.             while (go)
    8.             {
    9.                 yield return go;
    10.                 go = go.parent;
    11.             }
    12.         }
    13.         public static string Quoted(this string s) => s == null ? "null" : $"\"{s}\"";
    14.         public static int Count<T>(this IEnumerable<T> container, T v) { return container.Count(x => EqualityComparer<T>.Default.Equals(x, v)); }
    15.         public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> actor) { foreach (var x in source) { actor(x); } }
    16.  
     
    reuno likes this.
  49. dhousky_unity

    dhousky_unity

    Joined:
    Mar 21, 2023
    Posts:
    2
    @Stephan_B I am experiencing similar issues on 3.2.0-pre.6. I update a label's text, use ForceMeshUpdate(true, true), and then try to access the line info and it is all still at default values (mostly 0s). This is resolved after a single frame, but I would like to react to the line info immediately upon text update.
     
    Last edited: Feb 28, 2024
  50. lucbloom

    lucbloom

    Joined:
    Mar 27, 2020
    Posts:
    44
    Then you'll love my answer. ^