今回はTextMeshProやTextMeshProUGUIで扱えるプロパティ、textInfoを使っていて困ったことと対処方法を書いていきます。
※TextMeshPro自体の使い方や説明は省きます。
環境
Windows 11
Unity 2021.3.16f1
URP 12.1.8
TMP_TextInfoとは
TMP_TextInfoとは、TextMeshProやTextMeshProGUIが継承しているTMP_Textクラスに存在するクラスの一つです。
このTextInfoクラスには文字数だったり、テキストメッシュの座標情報が格納されていて結構使い勝手が良いです。
ここからは使っていて困ったことと解決策を書いていきます。
ちなみにTextMeshのメッシュが板ポリだったってことに気が付くのは結構最近のお話笑
characterInfoやmeshInfoの中身が上手く取得できない
困ったことの1つ目です。Debug.LogでcharacterInfoを出力してみても文字が入ってなかったりクラス自体がNullだったりします。
準備としてTextMeshProUGUIを用意しました。
NGコード×
public class TextInfoBlog : MonoBehaviour
{
public TextMeshProUGUI uiText;
void Start()
{
TMP_TextInfo textInfo = uiText.textInfo;
Debug.Log(textInfo.characterInfo[0].character); //出力:空
}
void Update()
{
}
}
原因は呼び出しタイミングが早すぎることです。
対処法
AwakeやStartからの呼び出しで使わないこと
OKコード〇
public class TextInfoBlog : MonoBehaviour
{
public TextMeshProUGUI uiText;
void Start()
{
}
void Update()
{
if (Input.GetKeyUp(KeyCode.G))
{
TMP_TextInfo textInfo = uiText.textInfo;
Debug.Log(textInfo.characterInfo[0].character); //出力:N
}
}
}
AwakeとStartから呼び出すとInspectorで文字が入っていたとしてもcharacterInfoやmeshInfoが正しく呼ばれることはありません。
必ず、Startよりあとのタイミングで呼び出すようにしましょう
新しく設定したtextのtextInfoが取得できない
2つ目の困ったことは新しくtextを設定したとき、最新のtextInfoを取得してこないという問題があります。
NGコード×
※他は同じなのでUpdateのみ記述
void Update()
{
if (Input.GetKeyUp(KeyCode.G))
{
uiText.text = "Textinfo test";
TMP_TextInfo textInfo = uiText.textInfo;
Debug.Log(textInfo.characterInfo[0].character); //出力:N
}
}
「T」という文字が取得したいのにデフォルトの「New Text」の「N」が出力されます。
対処法
GetTextInfo(newText)関数を使う
OKコード〇
void Update()
{
if (Input.GetKeyUp(KeyCode.G))
{
TMP_TextInfo textInfo = uiText.GetTextInfo("Textinfo test");
Debug.Log(textInfo.characterInfo[0].character);
}
}
TMP_TextInfo textInfo = uiText.GetTextInfo(newText)とやると内部的にtextも「newText」にセットできるし、「newText」のtextInfoも取得できる。(Get関数なのに内部でtextをSetしているのは少しわかりにくいですが関数の中身を見たら気が付けます...)
他にも見つけた便利なプロパティ
1文字の中央座標を取得する
よく紹介されている手法として、textInfo.meshInfoから全てのvertex(頂点)情報を取得後4を掛けてn文字の中央座標を求めることができます。
以下のコードでは「New Text」の3文字目、「w」の中心座標を求めて上からspriteをかぶせています
コード
public class TextInfoBlog : MonoBehaviour
{
public TextMeshProUGUI uiText;
public Image imagePrefab;
void Update()
{
if (Input.GetKeyUp(KeyCode.G))
{
TMP_TextInfo textInfo = uiText.textInfo;
var mesh = textInfo.meshInfo[0];
var vertices = mesh.vertices;
var charMechStart = 2 * 4;
var topleft = vertices[charMechStart];
var bottomRight = vertices[charMechStart + 2];
var center = (topleft + bottomRight) / 2;
var image = Instantiate(imagePrefab, uiText.transform, false);
image.transform.localPosition = center;
}
}
}
参考:uGUI Textの文字の座標を取得してインライン画像を実現する - おもちゃラボ
上記コードはNGというわけではありませんがもっと簡単な方法があったので紹介します。
characterInfoのtopleftやbottomrightといった座標プロパティが存在するのでそこから計算すればok。
改善コード(Updateだけ)
やってることは同じです。
void Update()
{
if (Input.GetKeyUp(KeyCode.G))
{
TMP_TextInfo textInfo = uiText.textInfo;
var topleft = textInfo.characterInfo[2].topLeft;
var bottomRight = textInfo.characterInfo[2].bottomRight;
var center = (topleft + bottomRight) / 2;
var image = Instantiate(imagePrefab, uiText.transform, false);
image.transform.localPosition = center;
}
}
正しい文字数を表示する
textInfo.characterCountのカウントは残念ながら思った通りには返ってきません。
例えばhoge hoge¥nfuga fuga
というtextが存在すると、このテキストの正確な文字数は16文字
です。空白も改行も数えてしまいます。
TMP_TextInfo textInfo = uiText.GetTextInfo("hoge hoge\nfuga fuga");
Debug.Log(textInfo.characterCount);
なのでこういう時はスペース数を取得して引いたものが正しい文字数です。
TMP_TextInfo textInfo = uiText.GetTextInfo("hoge hoge\nfuga fuga");
Debug.Log(textInfo.characterCount - textInfo.spaceCount);
なぜ改行もスペースとして数えられるのかはわかりませんがこれで正しい文字数は出せました。これをループに使えば各文字に何らかの処理を行えます!
いかがだったでしょうか、textInfo便利なのでぜひ使ってみてください。