Your imported TMP font looks weird, kinda off from the
RectTransform you had setup, or broke when using multiple languages that falls back to other TMP asset? Let’s not rely on extracted data and understand it completely in this article.
What to adjust
Here’s our workspace. We will start from everything at 0.
Fallback fonts must-know
Before we start.. to make our game sells we must support multiple languages. Notice the fallback font list. This font does not have Japanese character, so it needs to go to an another texture. But for now we will use just English and Thai font. But be aware that :
- Fallback fonts must share the Line Height with the root font.
- It can have unique everything else, Scale being the most important as you must use it to “reconcile” with main font’s line height, along with its own everything else.
Alignment & Baseline relationship
What you see is top, middle, and bottom align with equal RectTransform matching the Unity grid.
The top align isn’t quite the expected result, I expected the top edge of character to be at the top box. But, the meaning of Top alignment is actually : put the baseline at the top edge of the RectTransform .
So.. top align will not be “in the box” but bottom align is. Also, center align is not so “center” as when you put the baseline at center the font sticks up from it. Let’s fix these.
- Baseline affects all alignment.
- Ascender do not affect bottom align. Only positive value is valid.
- Descender do not affect top align. Only negative value is valid.
So what I want to do here is the bottom align should have the descender sticking out of the box but the top and center align should be moved down a bit. So I want to adjust only Ascender.
Note that definition of TMP’s “Ascender” is not the same with some other source’s “Ascender”. From this two images, TMP’s Ascender is equal to the first image’s cap height.
Here’s the result of adjusting just Ascender. Top align is at the top edge, middle aling has the baseline at center of most characters, bottom align untouched.
Line Height — Alignment — Baseline
Actually the 3 on the left has 3 more lines in each. Line Height affects how baseline are spaced out from each other. If there is no line height each line’s baseline would be at the same position.
Remember that all fallback fonts will share Line Height with the root font. We will counter-adjust that later with scaling.
With only Line Height and Ascender, I could achieve this result.
Fallback font adjustment
Fallback font means in the TextMeshProUGUI inspector we are using the same font as previously used, but because the character isn’t there it will load up an additional texture for those fonts.
First adjust the scale to match the first font. Also notice that Line Height has no effect at all!
Then you notice something wrong where there is only fallback font. The last line certainly does not looks right. If I add an empty line (press enter once) then it would looks right. What’s going on?
Lowest/Highest common Ascender/Descender
When multiple TMP are mixed, it choose the highest or lowest Ascender/Descender PER LINE order to “support” all fonts required. Remember again that Line Height are fixed to the root font.
What’s happening here is, only the last line does not need the root font’s texture. Why? Because all prior line has the carriage return which maps to the root font, and dragged the root font’s Ascender in order to support it.
That is it is not related to a new line at all. I can just press space bar once instead of enter and it would inherit the highest required ascender.
But anyways, the correct setup is to properly set the ascender for Japanese font (not 0 like this), so that when there is only Japanese the ascender would be correct. But here’s the problem. Line Height is inherited from the root font regardless of required texture. So we would best set the Ascender/Descender to the same as the root font even if Japanese character does not need any Ascender.
Then the problem will be, the Japanese text will be kinda too spaced out because it is assuming Ascender of other languages. Also line height is assumed from the other language too. This is the problem of using fallback font. (You want to assign only one TMP font to all TextMeshProUGUI and let it handle the texture selection)
Fallback font is not for localization
Then I realized that fallback font is probably for missing characters, like emoji or some weird marks. For localization the best way is to manually change the font field on the TextMeshProUGUI according to the language. And so you have a separated line height now.
Currently I have font A with English and Thai glyphs, and font B with Japanese glyphs. The “correct” way of doing this is to make 3 TMP assets.
- TMP Asset X : Using font A, only English character. Baseline/ascender/descender for only English, no fallback.
- TMP Asset Y : Using font A, only Thai character. Baseline/ascender/descender for Thai, fallback to TMP Asset X. When a line with mixed Thai/English came when the game’s language is Thai, we would get a spaced out with room for Thai’s ascender/descender. When the game is English, it would use just the TMP Asset X with tighter line height.
- TMP Asset Z : Using font B, Baseline/ascender/descender for Japanese. Fallback to TMP Asset X for occasional English and numbers.
You see even if Thai and English was from the same font file, I made a separated asset so that when the game is truly English the line space looks nicely packed. In Thailand we often mix up English and Thai in the same line, unlike for example Japanese where they prefer using just Japanese and Katakana for English words. So, when the game language is in Thai, the separated TMP asset would provide more line spacing. Multiple lines with only English would look kinda too spaced out but that would be rarer when the game is in Thai localization.
The appropriate use of fallback font is, you want those number fonts in the English font while in Japanese. This is a good use, because there is no number included in the Japansese font it falls back to grab just numbers from English font. (Or you think the numbers from Japanese font is ugly/looks out of place)
Also it uses Line Height from Japanese font because numbers are compacted and not requiring ascender/descender space.
In this image, Japanese font grabs number texture from English font while still using Japanese Line Height.
Notice that also the material that it uses the one of the main font. Because I tuned the dilate and outlines according to Japanese font’s texture size (much smaller than English, since there are a lot of characters to pack) the outline on those number looks a little bit thinner because the Japanese material settings is being applied to English’s bigger texture.
You can fix this by telling TMP to pack the font at fixed size rather than trying to do the biggest that fits the texture. Make your English/Thai/whatever low character count language to the same size with Chinese/Japanese/Korean/whatever high character count language’s size that fits in a reasonable texture size. (Like 1024x1024) For example, I required 1000 Kanji characters. I could afford up to 1024x1024, and turns out each character is at size 16 to fit the texture. Then I go to English and set the size as 16 even if English have very low character count. That way when “material fallback” happen it can looks similar. (I also think lower size's smearing effect of SDF looks kinda "artistic")
Then use a tool like I2Localization to change the assigned TMP asset based on your game’s current language. I think that’s the way to go.
After reconciling all languages and all mix-up cases, we can get consistent and expected behaviour in the TMP box.