Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Text!

Discussion in 'Works In Progress - Archive' started by Jasper-Flick, Jan 27, 2012.

  1. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    This product is now available on the Asset Store.

    So I made my own solution for sprite text, like many did before. Started simple, but you know how it goes... So what features are we looking at here?

    Imports BMFont FNT file plus PNG, kerning data included. There are multiple free tools to convert TTF to this format.

    $text_justify.png

    Justify alignment, along with the usual Left, Center, and Right modes. Achor at the default nine positions, plus baseline. Real tabs. Word wrap.

    $text_maps.png

    A distance map generator, using the Anti-aliased Euclidean distance transform algorithm described by Stefan Gustavson and Robin Strand. It extracts nice distance maps from low-res anti-aliased textures. With this, you can get good quality graphics and effects, as described in "Improved Alpha-Tested Magnification for Vector Textures and Special Effects" by Chris Green from Valve (SIGGRAPH 2007).

    $text_glyph_sizes.png

    It can handle weirdly sized glyphs just fine. If they overlap, later sprites are drawn on top of previous ones.

    $text_bmp.png

    All Basic Multilingual Plane characters are supported. Basically, anything that fits into one .NET's two-byte char value. Of course, you need a font that includes the characters you want.

    $text_inspector.png

    It's fully functional in editor mode, just like Unity's own TextMesh.

    The text component uses a regular mesh and acts as its own little sprite manager. The mesh data is fully accessible, so you could do post-processing on it if you want to. Performance is good, there's no temporary object allocation going on while sprites are being positioned and only the mesh data is stored.

    Now What?

    It still needs polish and I'm working on a few more things, including StringBuilder support for highly dynamic text, some post-processors, shaders, and helpers.

    So, would anyone buy this if I put it on the Asset Store? If I do so, I really don't know what to call this thing yet... Too generic a name will clash with other stuff out there, like EZgui's SpriteText. The lack of namespaces really hurts here. Would a company prefix be acceptable, like a plain CCText?
     
    Last edited: Feb 28, 2012
  2. Diviner

    Diviner

    Joined:
    May 8, 2010
    Posts:
    677
    Interesting. Can it wrap strings as well?
     
  3. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Yes, it word-wraps! I just realized I didn't mention that. You beat me to it!
     
  4. jcarpay

    jcarpay

    Joined:
    Aug 15, 2008
    Posts:
    561
    Nice! How is the performance on mobile devices?
    The StringBuilder sounds interesting!
     
  5. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    There's some overhead whenever you update the text, beyond that it performs like any other mesh on mobiles. So keep triangle count (non-whitespace character count) down, don't use expensive shaders, keep the texture small, and update as little as possible. If you don't change the text, the thing just sits there and does nothing.

    In case you do want to update frequently, I did just add StringBuilder support. This really helps because it obviates constant allocation of string objects, which would otherwise pollute memory and cause garbage collection runs.

    I also made an example post-processor that modifies vertex colors.

    $text_colored.png

    It adds a little gradient effect and gives everything that's between square brackets an alternative color. The brackers aren't shown because I excluded them from the font definition. The full text reads "You should visit [grandma]! She lives in the [house] near the [beach]."
     
    Last edited: Jan 28, 2012
  6. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    I think I'll just call this thing "TextBox".

    The boxes have a fixed width, which enables word wrapping. Height is dynamic. I made two bounding modes that control how the text is placed. In "Caret" mode, the text caret is constrained to the specified width, but oversized characters can stick out of it. In "Margin" mode, the text is padded to prevent the characters from poking out. Depending on the font, both modes can be equal or very different.

    $text_bounding_caret.png $text_bounding_margin.png

    I also added some helpers to get ints and floats into StringBuilders without causing string pollution.

    $text_formatted_float.png
     
  7. JamesArndt

    JamesArndt

    Joined:
    Dec 1, 2009
    Posts:
    2,932
    Wow great work!
     
  8. Diviner

    Diviner

    Joined:
    May 8, 2010
    Posts:
    677
    What are you using to display the text? TextMesh?
     
  9. kellygravelyn

    kellygravelyn

    Joined:
    Jan 22, 2009
    Posts:
    143
    I would be interested in buying this if:

    1) It can produce nice, crisp text in both orthographic and perspective views (sounds like it can since you're using that Valve technique).
    2) It can mix font types/colors in one string (e.g. bold the first letter of a word and make every other word green). It's super convenient to not have to manually position multiple objects to get these kinds of effects.
    3) You have a good solution for displaying numbers that change very frequently (scores and timers) without generating garbage (either through an advanced string builder or by taking in an array of characters). That's the one pain point I'm hitting with ex2D's fonts is that my score changes frequently (every frame since it's a ticker-style score) but each time I generate a new string and thus garbage.

    As an aside you can wrap a namespace around C# classes in Unity. I do this for a few of my types to avoid collisions with other people's code. It's not required, but it's definitely supported. That said I don't know how that works when using Javascript or Boo. That said I also don't mind the prefix approach since nobody seems to use namespaces and, after using the iOS SDK, I've gotten kind of used to it. :)
     
  10. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Diviner: The component uses a normal Mesh, MeshFilter and MeshRenderer.

    nickgravelyn:
    1) Sure, it can do that!

    2) It's limited to a single font definition, so beyond getting creative and packing variant styles and types in a single font you can't get true font and style mixes. Having said that, you can get pretty far with custom modifiers and shaders.

    $text_bold_alternate.png

    The above effect was created with a custom modifier that changed vertex colors for alternating words and used the alpha channel to flag the first letter of each word. A custom shader then used the distance map and the flag to produce either normal or bold characters.

    3) Yes, it works with StringBuilder and I made helper methods to convert ints and floats without generating garbage. I like my memory clean too!

    While namespaces do compile, the Unity editor cannot handle components inside namespaces and refuses to attach them to game objects. I haven't investigated beyond that hurdle.
     
  11. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    I usually get around this by wrapping everything in namespaces and having a component that inherits from those classes. Or, you can wrap everything except the top-most object (the component itself).
     
  12. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    It's only good if everything is in the namespace. But we can make do without. I'm currently cleaning up the project and adding docs.
     
  13. JRavey

    JRavey

    Joined:
    May 12, 2009
    Posts:
    2,377
    I'm quite interested in this, but I have to see how the next GUI works. In hindsight, I believe I should have gone with something custom instead of trying to use the Unity GUI, although I wouldn't need anything this fancy.
     
  14. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Waiting for the next Unity GUI might take a while. Use existing solutions if you want to get anything done. But keep in mind that I'm not making an entire GUI system here. I stick to the text box only, but make it a good one.

    :) Getting editor-mode updates to play nice with prefabs required jumping through a few hoops, but I passed that hurdle. Making stuff production-worthy always requires far more work than you think.
     
  15. hima

    hima

    Joined:
    Oct 1, 2010
    Posts:
    183
    I'm interested in this. I don't mind CCText at all, coming from Objective-C, I'm kinda used to it already :)
     
  16. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Ok I might just use CCText then.

    Another thing I'm on the fence about is how to expose variables. I could either

    1) use public variables, requiring an additional explicit method call to enforce constraints and propagate changes to the mesh;
    2a) use properties disguised as variables, checking constraints and propagating changes immediately on each value change;
    2b) as 2a, but use CamelCase property names according to .NET guidelines, but inconsistent with Unity's approach.

    I'm currently using 2a. Regardless of approach, changes via the inspector propagate immediately.
     
  17. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    I've decided to include a gradient tool I made into the project. It's convenient. You can either directly access it in code or export it as a texture and use that.

    $text_gradient.png

    Here's a shot of the distance map generator. Essential stuff if you want sweet font rendering and special effects. Can also be accessed in code.

    $text_distance_generator.png

    This is the font data asset. Not that exciting, but quite important. Imports BMFont-compatible FNT files, both text and XML format.

    $text_font.png

    And here's the CCText inspector, showing a prefab instance.

    $text_cctext.png

    That's it for version 1.0 features. All that's left now is more polish, documentation, and a demo project.
     
    Last edited: Feb 7, 2012
  18. jcarpay

    jcarpay

    Joined:
    Aug 15, 2008
    Posts:
    561
    Cool stuff!
     
  19. Muckel

    Muckel

    Joined:
    Mar 26, 2009
    Posts:
    471
    If i now use SpriteText to display a Raycast hit info.distance Value in my GUI i get around 8 % and 123 B GC ALLOC in Profiler...
    Can i use your Solution to display this example with less power consumption ?

    Here is a Test Script i use with EZGUI to test the Performance:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class TestText : MonoBehaviour {
    5.    
    6.    public SpriteText text;
    7.    
    8.    public string storyline = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG! 013456789";
    9.      
    10.    private int i = 0;
    11.    private int l = 20;
    12.    private int sl;
    13.    
    14.    void Start() {
    15.       sl = storyline.Length;  
    16.    }
    17.    
    18.    // Update is called once per frame
    19.    void Update () {
    20.       i++;
    21.       l = 20;
    22.      
    23.       if (i >= storyline.Length) i = 0;
    24.       if (sl - i < l) l = sl - i;
    25.       text.Text = storyline.Substring(i,l);
    26.    }
    27. }
    and here is the onGUI Version to test with Unity Text:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GUITextTest : MonoBehaviour {
    5.    
    6.    public GUIText text;
    7.    
    8.    public string storyline = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG! 013456789";
    9.      
    10.    private int i = 0;
    11.    private int l = 20;
    12.    private int sl;
    13.    
    14.    void Start() {
    15.       sl = storyline.Length;  
    16.    }
    17.    
    18.    // Update is called once per frame
    19.    void Update () {
    20.       i++;
    21.       l = 20;
    22.      
    23.       if (i >= storyline.Length) i = 0;
    24.       if (sl - i < l) l = sl - i;
    25.       text.text = storyline.Substring(i,l);
    26.    }
    27. }
    Watch profiler and if this is better i will buy your solution directly :)
     
  20. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Ok, I'm looking at the profiler line for GUITextTest.Update() with nothing else but a camera active. I don't have EZGui so can't test that.

    Running your GUIText version, I get up to 54B memory allocation and it claims around 7%. The memory allocation is caused by calling Substring() all the time.

    Using CCText instead with the same Substring() approach makes no difference for memory allocation. However, memory allocation drops to zero when I use a Stringbuilder and fill it each frame by iterating through the storyline. It currently takes between 10% and 20%, I haven't deeply profiled an optimized it yet.

    So, is it better? For memory, definitely. For CPU, it's currently a bit worse.
     
    Last edited: Feb 7, 2012
  21. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Fooling around more with more profiling... it's tricky business.

    Previous post's data was measured without the game view visible in the editor. With the game view visible, things change.

    Using GUIText with Substring

    On average, it seems GUITextTest.Update requires 1% - 7% and 54B. Meanwhile, Camera.Render eats 40% - 50%, drilling down shows GUIText.Render requires 23% - 26%, most of which goes to Mesh.SubmitVBO.

    Total is 24% - 33% and 54B.

    Using CCText with Substring

    On average, it seems GUITextTest.Update requires 15% - 35% and 54B. Meanwhile, Camera.Render eats 27% - 30%, drilling down shows MeshRenderer.Render requires 7% - 9%, most of which goes to Mesh.DrawVBO.

    Total is 22% - 44% and 54B.

    Using CCText with StringBuilder

    On average, it seems GUITextTest.Update requires 20% - 40% and 0B. Meanwhile, Camera.Render eats 24% - 33%, drilling down shows MeshRenderer.Render requires 7% - 9%, most of which goes to Mesh.DrawVBO.

    Total is 27% - 49% and 0B.

    For these tests, the only other things that are eating resources are the Unity engine, editor, and profiler. Let's do these test again with 30 spinning cubes in the scene.

    Using GUIText with Substring

    GUITextTest.Update: 1% - 2% and 54B
    GUIText.Render: 7% - 8%
    Total: 8% - 10% and 54B

    Using CCText with Substring

    GUITextTest.Update: 9% - 15% and 54B
    MeshRenderer.Render: 2% - 3% (CCText is transparent while cubes are opaque, so I can pinpoint it)
    Total: 11% - 18% and 54B

    Using CCText with StringBuilder

    GUITextTest.Update: 10% - 14% and 0B
    MeshRenderer.Render: 2% - 3%
    Total: 12% - 17% and 0B

    It appears that GUIText.Render's separate rendering path requires some overhead.
     
  22. Muckel

    Muckel

    Joined:
    Mar 26, 2009
    Posts:
    471
    look's very GOOD !
    will buy it !
    go on with it !!!

    thank you for making the Test's !

    P.S. I Love A'Dam... Paradiso rocks ! and Greenhouse Seed's too ;-)
     
  23. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    I did some heavy profiling and optimization. Here are the new results for the 30 cubes test.

    Using CCText with Substring

    GUITextTest.Update: 4% - 6% and 54B
    MeshRenderer.Render: 3% - 4% (as it's relative, Render went up because Update went down)
    Total: 6% - 10% and 54B (improved by 5% - 8%)

    Using CCText with StringBuilder

    GUITextTest.Update: 7% - 9% and 0B
    MeshRenderer.Render: 3% - 3%
    Total: 10% - 12% and 0B (improved by 2% - 5%)

    P.S. Yea, Paradiso is a nice place. :)
     
  24. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    I've made a demo scene to put into the store package (fonts not included). It presents a quick means to fool around with various settings and shaders.

    $text_demo.png

    I also added a final shader called "Smooth Fade". Basically, it makes text vague based on view distance, while keeping it visible. Kinda like how it works in real life.

    $text_demo_fade.png
     
    Last edited: Feb 10, 2012
  25. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Here's the demo running on an iPad!

    $text_ipad.jpg

    I'm now working on some nice documentation.
     
  26. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    This is far more comprehensive to my home brew solution which uses .fnt files. I work with mostly mobiles and have a couple of questions:

    1. is it 1 mesh generated or lots of little quads?

    2. do you regenerate the entire mesh when text changes? mine does not. Instead I create a text mesh with empty quads which are a finite amount of chars. So if I call DrawText and the count is over that many chars those won't display.

    By being able to create a mesh ahead of time, I only need to change my locally stored mesh, then upload. I don't need to fetch the mesh, change it, or generate a new mesh on the fly.

    That represented some big gains for my game. While I am tempted by the feature set of your library (blows mine away), I am a little worried it isn't as fast as it could be.
     
  27. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Think SpriteManager.

    1. It's one mesh per CCText. It uses four vertices and two triangles per non-whitespace character.

    2. The mesh grows in chunks as needed and collapses triangles which are currently unnecessary. So it only regenerates if it's no longer big enough. Typically, a text change only updates existing vertices and uv and there's no object creation going on.

    :) You needn't worry, I made this thing with mobile in mind.
     
  28. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    That's damn sweet. One last question, do you fetch the mesh like in the unity examples, modify then submit new mesh, or do you keep a local 'mirror' stored? I would think eliminating the fetch part is going to be a speed gain too ie no temp arrays etc.

    oh! and also we can get to the base .gameObject right? so I can fiddle with layers and parenting and so on to suit my game?

    Could you perhaps also post some code with your system, or quick example how to set up a high score shown in game?
     
  29. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    CCText keeps a reference to the mesh object. It's all thoroughly optimized. No component lookups after start and no object allocation except when the mesh needs to grow.

    CCText is a MonoBehaviour so you can do all the usual stuff with it. It's just another component on a GameObject that also has a MeshFilter and MeshRenderer. If you want to do stuff like adjust colors or vertices after a text update, there's a convenient modifier hook for that too.

    I'm in the process of writing a nice online documentation. The package will also include the demo shown above, which is a simple example of manipulating the text through scripting. I'll probably make more examples too, like a high score list, rolling credits, etcetera, and include them in the online documentation.
     
  30. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Did a final round of optimization. Now the string and StringBuilder versions are virtually equal in performance, with the added advantage of not allocating any memory in the case of StringBuilder.

    Here are profiler results for the 30-spinning-cubes test, with deep profiling disabled. I added a test for TextMesh.

    Using GUIText with Substring

    GUITextTest.Update: 2% - 2% and 54B
    GUIText.Render: 10% - 11%
    Total: 12% - 13% and 54B

    Using TextMesh with Substring

    GUITextTest.Update: 6% - 10% and 54B
    MeshRenderer.Render: 5% - 5%
    Total: 11% - 15% and 54B

    Using CCText with Substring

    GUITextTest.Update: 2% - 3% and 54B
    MeshRenderer.Render: 5% - 5%
    Total: 7% - 8% and 54B

    Using CCText with StringBuilder

    GUITextTest.Update: 2% - 3% and 0B
    MeshRenderer.Render: 5% - 5%
    Total: 7% - 8% and 0B

    Basically, in this contrived example CCText equals GUIText in update but beats it in rendering, and CCText equals TextMesh in rendering but beats it in update.

    So it seems that CCText performs quite well for short dynamic content. But what about big chunks of text?

    Here's another 30-spinning-cubes test, this time alternating each frame between displaying static strings containing Lorem Ipsum once and twice.

    Using GUIText

    GUITextTest.Update: 2% - 3%
    GUIText.Render: 23% - 33%
    Total: 25% - 36%

    Using TextMesh

    GUITextTest.Update: 28% - 38%
    MeshRenderer.Render: 6% - 6%
    Total: 34% - 44%

    Using CCText

    GUITextTest.Update: 14% - 20%
    MeshRenderer.Render: 7% - 8%
    Total: 21% - 28%

    Looks like CCText does a good job, especially considering it word wrapped while the others didn't.
     
    Last edited: Feb 13, 2012
  31. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    I've just released the distance map generator tool as a free package (it's under GNU GPL).

    You can find it here.

    Let me know if there's anything you don't like about the online documentation. Everything else will be documented using the same format.
     
    Last edited: Feb 17, 2012
  32. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Hi,
    Thanks for sharing this. Runevision did something similar here too.
     
  33. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    My pleasure!

    Yea, I'm not the first to make a distance map generator. But I do seem to be the first here to take advantage of Stevan Gustavson's and Robin Strand's work.

    Still working on the documentation, by the way.
     
  34. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
  35. kellygravelyn

    kellygravelyn

    Joined:
    Jan 22, 2009
    Posts:
    143
    That looks pretty solid! Should be a pretty good release when it's ready.

    Off-topic: how did you get the custom icons for the assets and components? I've yet to find that in the Unity documentation. :)
     
  36. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    :) Thanks!

    If you have a component or asset script named "MyThing", then put a texture named "MyThing icon" in the Assets/Gizmos folder of your project (create one if it doesn't exist). Unity will start using that texture as an icon for "MyThing". As Unity does some weird caching, it might take a while and some restarts for all objects to pick up this change.

    You can't place icons anywhere else, it must be exactly in Assets/Gizmos.

    The small icon size is 16x16. The large asset icon size is 40x40. If an asset has a preview, it will be used for the large icon.

    If this stuff is documented anywhere, I haven't found it either.
     
    Last edited: Feb 23, 2012
  37. kellygravelyn

    kellygravelyn

    Joined:
    Jan 22, 2009
    Posts:
    143
    Awesome info. Thanks!
     
  38. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    :) Sure, no problem!

    It's getting close to release! I've made a new topic in the Asset and Asset Store forum.
     
  39. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
  40. Jasper-Flick

    Jasper-Flick

    Joined:
    Jan 17, 2011
    Posts:
    957
    Silly me posting in the wrong topic...