• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by DOZO · Dec 13, 2014 at 01:59 AM · uitext

Is there a uGUI replacement for GUIStyle.CalcSize?

In Unity 4.6, I have an uGUI text box in which I want to gradually display a paragraph of text one letter at a time. However, when I do this, there's an undesirable 'flickering' effect at the end of each line, as words grow too long and are automatically wrapped to the next line.

For example, the text box will first display:

 This is a very long line that exceeds the wi

and then moments later:

 This is a very long line that exceeds the
 width

My first instinct on how to avoid this issue is to calculate the estimated width in pixels of the string being displayed, and manually insert line breaks before the next word to be displayed when it would cause the line to exceed the width of the textbox and wrap. Using the old system the GUIStyle.CalcSize function could perform this task. However, I do not see any equivalent function in the new UI system.

Do I have any alternative to creating a legacy GUIStyle for the sole purpose of calculating line width?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

4 Replies

· Add your reply
  • Sort: 
avatar image
0

Answer by DOZO · Dec 14, 2014 at 01:39 PM

Since the answer seems to be "no," for the benefit of anyone who stumbles across this question later looking for a similar solution, the one I went with was this subroutine that I call using "while (RequestText (out newText))" from the coroutine displaying the text (make sure the text object has word wrap turned off):

     public bool RequestText(out string newText) {
         char[] s = fullText.ToCharArray ();
         if (textPosition >= fullText.Length) {
             newText = "";
             return false;
         } else if (s[textPosition] == ' ') {
             // grab text until we hit another space
             newText = " ";
             int i = 1;
             while (textPosition+i < fullText.Length && s[textPosition+i] != ' ') {
                 newText += s[textPosition+i];
                 i++;
             }
             txt.GetComponent<UnityEngine.UI.Text>().text = partialText+newText;
             if (txt.GetComponent<UnityEngine.UI.Text>().preferredWidth > txt.sizeDelta.x) {
                 // the next word is too long; insert a line break
                 newText = " \n";
             } else {
                 newText = " ";
             }
             textPosition++;
             return true;
         } else {
             newText = s[textPosition].ToString ();
             textPosition++;
             return true;
         }
     }

I was concerned that actually assigning the text to the textbox would cause flickering, but that doesn't seem to happen - I guess Unity is smart enough to not blit the text to screen until the next frame.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
2

Answer by Bunny83 · Dec 14, 2014 at 02:35 PM

The new GUI system is actually way more open than the old one. You can use the TextGenerator used by your Text instance. You can trigger the population of the cachedTextGeneratorForLayout TextGenerator by reading preferredWidth of your Text. Once done you should be able to read it's lines property which holds a cached List of UILineInfo structs, one for each generated line. With that information you should be able to split the whole text into lines at once.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image DOZO · Dec 14, 2014 at 03:18 PM 0
Share

Oh, thanks. I think I will stick with my 'look-ahead' approach though - I'll need something similar anyway to make rich text tags display instantly rather than be broken up into individual characters, I might add some fancy features like automatically hyphenating long words, and I suspect the difference in performance is negligible.

avatar image
0

Answer by SilentWarrior · Oct 20, 2015 at 10:39 PM

Hello.

Let me just add that you can in fact calculate how many characters until a new line is produced by two methods:

1 - If your font is monospaced (every character is the same length), you can add a fake character, get the Texts preferredWidth and then use that to divide the rectTransform width:

 public int CalculateTextWidthInCharactersMonospaced ()
     {
         string backupText = text.text;
         text.text = "M";
         var count = Mathf.FloorToInt (text.rectTransform.rect.width / text.preferredWidth);
         text.text = backupText;
         return count;
     }


2 - If your font is not monospaced, add a fake character in a loop while the Text width is bigger than the preferredWidth, then count the characters and subtract one (the one that caused new line) :

 public int CalculateTextWidthInCharactersNonMonospaced ()
     {
         string backupText = text.text;
         text.text = "M";
         while (text.rectTransform.rect.width > text.preferredWidth)
             text.text += "M";
         var count = text.text.Length - 1;
         text.text = backupText;
         return count;
     }

Both methods require that the Text component is already rendered on screen, so, if you want to calculate it at the Start() method you have to delay its execution for at least 1 frame, here is a full example that sets a public variable so you can see it in the editor :

 public int CalculatedMaxCharWidth = 0;
     Text text;
     void Awake ()
     {
         text = GetComponentInChildren<Text> ();
     }
     IEnumerator Start ()
     {
         yield return null;
         CalculatedMaxCharWidth = CalculateTextWidthInCharactersNonMonospaced ();
     }
     public int CalculateTextWidthInCharactersMonospaced ()
     {
         string backupText = text.text;
         text.text = "M";
         var count = Mathf.FloorToInt (text.rectTransform.rect.width / text.preferredWidth);
         text.text = backupText;
         return count;
     }
     public int CalculateTextWidthInCharactersNonMonospaced ()
     {
         string backupText = text.text;
         text.text = "M";
         while (text.rectTransform.rect.width > text.preferredWidth)
             text.text += "M";
         var count = text.text.Length - 1;
         text.text = backupText;
         return count;
     }

Hopefully this helps someone :)

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by spood · Sep 15, 2016 at 12:44 PM

Came across this since I have been trying to do scrolling text and had the same problem. I dug a bit deeper and found there is something we can use for a replacement for GUIStyle.CalcSize... at least for the purposes we need.

 textWeWantToDisplay = "this is just some text we want to display";
 
 TextGenerationSettings generationSettings = txt.GetGenerationSettings(txt.rectTransform.rect.size); 
 float textHeight = txt.cachedTextGeneratorForLayout.GetPreferredHeight(textWeWantToDisplay, generationSettings);
 Debug.Log("textHeight: " + textHeight);

Where our Text component is "txt". This will log the height of what our Text component will be for the given textWeWantToDisplay.

We can use this by constantly checking the height we have displayed to the screen so far before we write each word. After calculating the height with the new word, if we find that the calculated height is bigger then we can insert a '\n' new line character.

EDIT: Actually just quickly wrote this up now and it works great :) Can be improved but it's a nice start. To use it, when you're scrolling (or typewriting) your characters, if you detect a space character, " ", you can call doesNextWordWrap(). If it returns true, write a newline instead of a space.

 private bool doesNextWordWrap(string textParagraph, string entireTextParagraph, int index) {
     string nextWord = getNextWord(entireTextParagraph, index);
     TextGenerationSettings generationSettings = txt.GetGenerationSettings(txt.rectTransform.rect.size); 
     float originalTextHeight = txt.cachedTextGeneratorForLayout.GetPreferredHeight(textParagraph, generationSettings);
     float newTextHeight = txt.cachedTextGeneratorForLayout.GetPreferredHeight(textParagraph + nextWord, generationSettings);
     if(newTextHeight > originalTextHeight) {
         return true;
     }
     return false;
 }
 
 private string getNextWord(string entireTextParagraph, int index) {
     string textAfterParagraph = entireTextParagraph.Substring(index + 1);
     string[] nextWords = textAfterParagraph.Split(new [] {" "}, System.StringSplitOptions.RemoveEmptyEntries);
     if(nextWords.Length > 0) {
         return nextWords[0];
     }
     return "";
 }



On another note, I tried using the cachedTextGeneratorForLayout after reading it's preferredWidth, as @Bunny83 suggested.

 txt.text = textWeWantToDisplay;
 float discardme = txt.cachedTextGeneratorForLayout.GetPreferredWidth(textWeWantToDisplay, txt.GetGenerationSettings(txt.rectTransform.rect.size));
 UILineInfo[] lineInfo = txt.cachedTextGenerator.GetLinesArray();
 Debug.Log("line amount: " + lineInfo.Length);
 for(int j=0; j<lineInfo.Length; j++) {
     Debug.Log("line's character start index: " + lineInfo[j].startCharIdx);
 }
 txt.text = "";

It was delayed by one, so for example the first time I ran it, it didn't have any lines. But the next time I called it, it said it had 2 lines, when really I had 3 lines. This repeated, where it always showed the previous value for "textWeWantToDisplay". I'm sure a solution could be worked out of it, but I think it would be a bit messier because of the delayed value. A shame since knowing the character index makes this much easier, just pop in a new line character before each index.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

uGUI letter spacing and kerning 1 Answer

Text blurred: uGUI 4.6 9 Answers

How to get the current "best fit" size of a Text component? 3 Answers

UGUI Find tapped character in textfield? 1 Answer

4.6 UI Text rect does not expand automatically 2 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges