Set Size Based on Text Metrics (in Unity Engine)

Running with this theme of just sharing stuff that I think is cool, and wanting to share the wisdom. This post is about Text Mesh Pro in Unity Engine. Hopefully, it will be short…

I am trying to make a UI element fit “the size of text”. So instead of “fitting text to the UI element”, it’s the other way around.

Text Mesh Pro has an option called “Auto Size” which will automatically set the font size of text so it fits a bounding box area defined by the Rect Transform. In this case, I want to have a user-defined font size and have the bounding box area fit itself to the actual size of the text itself. Which would allow me to create a background frame for the text to sit inside. Useful for tooltips (in my case).

tooltip.gif

There are a couple of different ways this can be done, but I will say that if you try this for yourself. The end result may be a little wonky or have some issues. But it seems like the API provides everything necessary to achieve this effect.

Depending on what you're looking to do for your project, there are a couple of different ways you can code this to make it work.

But this is my implementation: (note, make sure Text Mesh Pro in your project is up to date)

I hate the naming they use here. “GetRenderedValues” isn’t really descriptive of what you’re trying to do.

I hate the naming they use here. “GetRenderedValues” isn’t really descriptive of what you’re trying to do.

If you tried to use this code exactly the way it is, you’re probably going to run into issues. But understanding what it’s doing will allow you to use it whichever way you want. Essentially, it is just finding out what the “text metrics” are. And using that data to set the “width” and “height” values of another rect. In this case, I am using an inspector property to set that. So this mono would be attached to an object that also has the Text Mesh Pro component “TMP_Text” (I hate that naming scheme, but I get why they did it). I will talk a little bit more about “the different ways this can be done” near the end. Anyway.

Alternatively, you could design this code so that the component modifies the width and height of the object it’s attached to, and you have to provide a reference to the Text component you want to base it on.

I might end up doing that anyway, makes more sense with what’s going on in the hierarchy.

Speaking of hierarchy…

ghgfdg.png

Originally, this part was forcing me to do some “wonky” things, but I managed to figure out a nice way to do this. But you may still end up with wonkiness when you try this. But I had to set up the hierarchy in a specific way to make things work.

In this case, the text object and its background are sibling objects, and the auto-sizing is set to the “tooltip” object. The description frame object will stretch to be the same size as the tooltip object, but the text object won’t change.

In other words, the background will be sized properly so it fits the text. I can also set some margin (or padding, I forget which it is in this case) in the frame object so there is plenty of space between the text and the background border.

This gif does a better job of showing what I’m saying.

tooltip2.gif

Lastly, I want to go over the API a little bit and why I was saying there are a few different ways to do this. And you should pick the one you want. A lot of them do seemingly the same thing, which is a bit annoying. But we have to deal with that and move on.

Also, there is very little scripting documentation. As far as I know, there is no official explanation of what some of these things are used for, so it’s all a matter of trial and error.

Anyway. If you did’’t know this, in Visual Studio you can mouse over a type in your code and press f12 to bring up a “metadata preview” of the class behind that type. Which will allow you to see all the publically accessible members (that’s what I’ve been calling the API, could probably call it something better).

So here it is.

I suggest you do this for yourself and explore, but if you look at the class metadata preview like I said, you will see these functions and properties. From my testing, they all doo seemingly the same thing. Except for one major difference.

“GetPreferredValues()” will give a Vector2 that is the size of a rectangle that would fit rendered text if there were no text wrapping.

Whereas all the others, like “bounds”, “textBounds”, “GetRenderedValues()” will give a size based on the actual rendered text. So if you use “GetPreferredValues()” with text and there are no newline characters in that text (all the text is on one line, no enter kay characters). Then the resulting Vector2 would be super long if you had a lot of text.

If you use “GetRenderedValues()” it will account for wrapping and be the correct size.

I also forgot to circle in the image, but there are also some properties called “preferredWidth” and “renderedWidth” and vice versa. Which by the naming must do the same thing as the functions, but there is no inline documentation that says which one I should use. So again, it’s a matter of finding the one that works for you.

Some of them, for some reason, don’t quite surround the entire text on the right edge for me, but others do. But like I said, just experiment with all this and see what works.

Oh and one last important thing to keep in mind.

before trying to get this data from the Text Mesh Pro component, you have to call

“ForceMeshUpdate“ because for whatever reason the data you get will be “old” and won’t update properly in the editor unless you click on something else. I don’t know if this is only in the editor, and if this line of code will actually affect performance at all. Which would be annoying.

If It’s possible, I would want to rewrite my code so my logic only fires when the text inside the component changes.

Anyway, I hope this information is useful to any of you. Being that there is a trend of Unity and its partners not always being spot on with their documentation. I figured this information might be useful to others who are walking down the same path as I am but run into a dead end. So I want to leave “breadcrumbs” on my path so the next person behind me doesn’t have to deal with the same crap.

Thanks for reading!

Next
Next

Followup - Previous ScriptableObjects Post