テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】TextMeshProで「数字」をテキストで表現する時にGC Allocを発生させない2つの方法

f:id:tsubaki_t1:20190204025051j:plain

今回はTextMeshProを使用する際に、UIに文章を表示する際のGCを抑えるアプローチについてです。

数字を表示する際にGCが発生するのを回避したい

「数字をUIに表示する」アプローチはよくある動作です。例えばスコアであったり、ダメージであったり、HPであったり、残金であったり。
一時は文字に頼らないUI表現が流行しましたが、現状は多くのケースで数値が採用されています。

f:id:tsubaki_t1:20190204010040j:plain
スコア表現

この数字を表示するアプローチ、場合によってはGCが発生します。正確には数字をStringに変換(int.ToString())のタイミングでGCが発生します。これを回避するための色々なアプローチはあるのですが、TextMeshProを使用するとかなり簡単に回避出来たので、それを紹介します。

SetCharArray(...)を使用する

ある意味、昔ながらのアプローチに近いのがTextMeshProUGUI.SetCharArray(...)を使用するアプローチです。

SetCharArray(...)は、stringではなくchar [ ](キャラクターの配列)やint [ ](文字コードの配列)を使用して文字を表現するAPIです。例えば下のような動作になります。

// 例1、結果:TEST1
int[] characters1 = new int[]{84, 69, 83, 84, 49};
label.SetCharArray(characters1, 0, characters1.Length);

// 例2、結果:Test2
char[] characters2 = new char[]{'T', 'E', 'S', 'T', '2'};
label.SetCharArray(characters2, 0, characters2.Length);

f:id:tsubaki_t1:20190204011721j:plain
例1の結果

f:id:tsubaki_t1:20190204011734j:plain
例2の結果

後の話は簡単で、SetCharArray(...)の引数となるintやcharの配列の中身を数字と書き換えてやれば、int.ToString()を使用せずとも数値を表現出来るという寸法です。

なおSetCharArray(int[], start, length)APIが要求するintの配列の中身はUnicodeです。これは普通にUnicode表を見れば乗っていますが、同様にTextMeshProのFontAsset>CharacterTableからも確認出来ます。表示は16進数ですが、要求する数字は10進数なので、そこは注意が必要ですが…

ヒント:Unicodeの0~9は、数字の48~57が該当します。なので、(数字+48)で数値を表現出来ます

SetText(...)を使用する

もう一つのアプローチが、SetText(...)を使用するアプローチです。こちらはString.Format(...)のような使い勝手で使用できるので、SetCharArray(...)と比較して圧倒的に楽に使用できます。

例えば数値を表示したい場合、下のようなコードで表示します。

// 例3
int value = 12345;
label.SetText("value : {0}", value);

f:id:tsubaki_t1:20190204015429j:plain
例3の実行結果

なお、コチラの場合は小数点やマイナスの表現のために一工夫する必要もありません。

// 例4
float value = -123.41234f;
label.SetText("value : {0:2}", value); // 小数点2桁まで表示

f:id:tsubaki_t1:20190204022131j:plain
例4の結果

エディターではGCが発生する点に注意

この機能で少し厄介なのは、エディター上で動作させている場合と、実際にビルドした場合で動作が異なるという点です。具体的には、エディターで動作させるとGCが発生します。

f:id:tsubaki_t1:20190204021147j:plain
ビルド時の動作と、エディターでの動作の違い

追ってみた所、どうもSetCharArray(int[], int, int)でint[](Unicode)を使用している場合、エディターのTextMeshProのInputBoxに文字を表示するため、IntToStringが呼ばれるみたいです。また他のケースでも何処かでGCが発生しています。

f:id:tsubaki_t1:20190204020505j:plain
InspectorのTextMeshPro設定画面

SetCharArray(...)のサンプル

数字を文字コードの配列に直す場合、桁数固定の0埋めなら下のような感じです。

gist.github.com

f:id:tsubaki_t1:20190204014238g:plain
0埋め時の動作

一方、0埋めでない時の動作はこんな感じです。桁数を求めてintを埋めてる感じです。

gist.github.com

f:id:tsubaki_t1:20190204014055g:plain
0埋めでない時の動作

charを使用する場合は、こんな感じで使えます。結果は0埋めと同じ動作です。

gist.github.com

SetText(...)のサンプル

gist.github.com

f:id:tsubaki_t1:20190204015938g:plain
SetTextの実行結果

#感想

派手に装飾した数字を出す時でも、色々と考えなくて良いのは楽で良いです。

関連

charを使用した場合

qiita.com

マイナスに対応したchar[]ベースのアプローチ

github.com