バーチャルキャストにおけるTextMeshPro deep dive(その１：タグ）

バーチャルキャストのVCIで文字を扱うために使用されるTextMeshProですが、VCI製作者の間であまり知られていない(当社調べ)タグ機能について紹介します。

# 文字をもっと細かく制御したい

unityのデファクト的なテキストオブジェクトとして知られているのがTextMeshProです。様々なプロパティを設定できる柔軟なコンポーネントなのですが、バーチャルキャストでは例によってさまざまな制約があります。VCIではテキストの内容しかいじることができません。
でもフォントも何もいじれないのでは味気ない...何とかして文字を装飾したり制御したりすることはできないのでしょうか。実はTextMeshProは、 **htmlタグのようなタグを使うことができます**。ある程度のことは **タグを介して制御することができる** のです。

### タグとは
タグとは文章内に<と>とで囲われたコマンドのようなものです。タグ埋め込むことで、文章の意味や見た目を制御する方法はあらゆるwebページで使われているhtmlなどで意識することはないかもしれないけどかなり身近に使われています。

### タグでできること
文字のサイズや色、配置などいろいろなことができます。unity上のインスペクタに出てくるオプションはかなりの部分がタグで機能を代替可能です。

## TextMeshProのタグ一覧

| タグ名 | できること | 対応 |
|:-:|:-:|:-:|
| br  | 改行  |   |
| align  | 水平方向のテキストの配置  |   |
| pos  | 水平方向のテキストの位置 |   |
| color  | フォントの色  |   |
| alpha  |  フォントの透明度 |   |
| b  | 太字にします  |   |
| i  | 斜体にします  |   |
| u  |  アンダーライン |   |
| s  | 取り消し線  |   |
| cspace  | 文字間隔を設定  |   |
| ~~font~~  | ~~フォントを設定します~~  | 非対応  |
| indent  | インデントを設定  |   |
| line-height  | 行間隔を設定  |   |
| line-indent  | 段落先頭のインデント |   |
| ~~link~~  |  ~~ハイパーリンクを設定~~ | 非対応  |
| lowercase  | 小文字にする  |   |
| uppercase  | 大文字にする  |   |
| allcaps  | 大文字にする  |   |
| smallcaps  | 小文字を小さな大文字で表示する  |   |
|  margin | 余白を設定する  |   |
| mark  | マーカーをつける  |  |
|  mspace | 等幅フォントにする  |   |
|  noparse | タグを無効化する  |   |
|  nobr | 勝手に改行しないようにする  |   |
|  size | 文字の大きさを設定  |   |
|  space | 水平スペース  |   |
| sprite | 画像を挿入 |  |
| style | カスタムスタイル  |   |
| sub  | 下付き文字  |   |
| sup  | 上付き文字  |   |
| voffset  | 垂直方向にオフセット  |   |
| width  | テキストの幅  |   |
| rotate  | 文字の回転  |   |
| page  | 改ページ |  1.8.2aで廃止？ |

# 解説

## タグの基本
”\<”と”\>”との間に名前を付けて表す文字の塊を **タグ** といいます。タグには二種類あります。単独でよいもの（埋め込み型）と、\<tag\>~\</tag\>の形で開始タグと終了タグで挟み込んで使うもの（領域指定型[^1]）の二つです。
また、タグによってはタグ名だけでなく何らかのパラメーターを持っていることがあります。textmeshproのタグでは、このパラメーターを=に続けてそのまま書きます（例えば、\<size=1\>のように）[^2]。

## \<br\>
改行をします。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text",""..
    "いろはにほへと<br>"..
    "ちりぬるを<br />"..
    "わかよたれそ<br/>"..
    "つねならむ")
```
##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/3aab7296-11ce-b715-5901-c1ce63c3cd9a.png)

xhtml形式の書き方(\<br /\>など)だとうまくいきません。微妙に改行できてしまっている部分もありますが、やめたほうがいいでしょう。

## \<align\>
文字列の位置を調整します。左寄せ、中央寄せ、右寄せ、均等割り付け。全体の文字レイアウトに。 **Geometry Centerは使えません** 。部分的に細かく制御したい場合は次のposを使ったほうが良いでしょう。
##### 使用例
```lua:main.lua
vci.assets.SetText("Text",""..
"<align=right>right<br>"..
"<align=center>center<br>"..
"<align=left>left<br>"..
"<align=flush>flush<br>"..
"<align=justified>justified<br>")
```
##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/8037d2df-86b7-6672-cef4-6e4554978f8c.png)

flushとjustifiedは両方均等割り付けなのですが、その違いは、flushtがどんな条件下でも均等割り付けを行うのに対して、justifiedは最終行だけ均等割り付けを行いません。ここでいう行というのは、brの影響を受けないようです。つまり、 **テキストの折り返しを有効** にして複数行にわたっていないとjustifiedの効果が見えてこないようです。テキストの折り返しがあるということはある程度詰まっているはずなので、これだけではノーマルと差が見えにくいですが、後述のnobrなどとの組み合わせることで差がつきます。使い分けは、記事などの長文で新聞のようレイアウトできるため、長文説明文ではjustifiedのほうが見栄えが良いと思います。

## \<pos\>
文字列の位置を調整しますが、こちらは一行のみです。細かく制御できます。使える数値はパーセンテージ、フォントサイズ、ピクセルで指定できるそうですが、ptとpx(とem)と単位なし裸数字の区別がついていなさそうです。仕様かバグなのかは不明。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<align=left>"..
"hoge<pos=20%>fuga<br>"..
"hoge<pos=5pt>fuga<br>"..
"hoge<pos=5px>fuga<br> "..
"hoge<pos=5>fuga")

```
##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/583ce878-1786-e0c4-cb68-404f363a8307.png)


折り返しを有効にした状態で長文の文頭につけると、文頭のみインデントされるよく見る文書形式になります。使いどころは色々ありそうです。
また、posはめちゃくちゃな値を入れると重なってしまいますが、逆に利用してやると次のようなこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/7279f529-e2ce-088f-d6f3-d7e0d806829b.png)


## \<color\>
色を変更します。色の指定には、あらかじめ用意された色の名前のほかに、16進数の色番号が指定できます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<color=red>楽しい<color=#55ff55>"..
"を居場所</color>に<color=#2255ff33>しよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/fa4162ca-2897-0e41-6032-1222a420dce8.png)

色の名前は以下のものが使えるようです
black, white, blue, green, orange, purple, red, yellow
また、色コードは６桁の場合htmlと同じく16進で#RRGGBBで指定されます（htmlと同様3桁に端折ることも可能です）。８桁の場合、最後の二桁はアルファになります。すなわち、#RRGGBBAAです。


## \<alpha\>
アルファ（透明度）を設定します。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","楽しいを<alpha=#77>居場所にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/e2b73c14-82a7-0cc4-0eec-51cd6ecac7c3.png)

colorと違って、こちらは予約されている名前もないですし、省略して１桁にすることもできません。また、パーセンテージも指定できず#AA形式のみです。


## \<b\>
太字体にします。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<b>楽しいを</b>居場所にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/592b7d1f-f919-c7c2-d098-cf77a3b6075f.png)

文字間隔も一緒に大きくなってしまうようです。

## \<i\>
文字を斜体にします。bなどと複合可能です。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<i>5000兆円欲しい！！！")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/7f0cae79-3a07-3446-0aa5-7808891fd31d.png)

こちらは文字間隔は変わらないようです。5000兆円ほしい。



## \<u\>
下線を引きます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<u>楽しいを</u>居場所にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/543c278f-3725-3f4e-d3ac-babd2b7291bc.png)

こちらも複合可能です。

## \<s\>
取り消し線をつけます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","5000兆
VCC
円欲しい！！！")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/fb7fe7ac-4ca3-6038-32ff-788d1d3ebe69.png)

こちらも複合可能です。VCCより円でほしいです。

## \<cspace\>
文字間隔を変更します。負の値も設定可能です。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<cspace=1>楽しいを</cspace>居場所にしよう<br>"..
"<cspace=-0.5><b>楽しいを</b></cspace>居場所にしよう")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/a3fe55c8-cf47-c836-3029-de46c0690df2.png)

２行目はbで広がってしまった文字間隔を戻しています。単位はドキュメントではemとされていますが数字でもいいようです。（全体的にemは省略されてただの数字になっているように見えますね）




## \<indent\>
インデントを設定します。箇条書きなどに便利です。１行だけならposでもできますが、indentは折り返しがある場合でも永続します。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<align=left>1.<indent=15%>いろはにほへと</indent><br>2.<indent=15%>ちりぬるを</indent><br>3.<indent=15%>わかよたれそ</indent><br>4.<indent=15%>つねならむ")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/77af55f3-9994-8c48-ef24-84065ad1e1ed.png)



## \<line-height\>
次の行の高さを指定します。入れられる数値はemとパーセンテージです。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text",""..
"<line-height=0.5>いろほへと<br>"..
"<line-height=1>ちりぬるを<br>"..
"<line-height=2>わかよたれそ<br>"..
"<line-height=1>つねならむ")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/5ae7d76d-1f39-1ea6-7156-3ee213d966c3.png)



## \<line-indent\>
行頭のインデントを指定します。よくある文書の行頭のタブです。折り返しを有効にしていないと効果がわかりにくいと思います（brするごとにインデントされてしまいます）。設定できる単位はemとパーセンテージです。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text (1)","<align=left><line-indent=1>いろはにほへとちりぬるをわかよたれそつねならむ")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/41c16762-71a4-936a-004f-1137f67c9295.png)




## \<lowercase\>
すべて小文字にします。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<lowercase>VirtualCast")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/9d0c76f7-805f-e851-40dc-6838a1a679a4.png)





## \<uppercase\>
すべて大文字にします。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<uppercase>VirtualCast")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/b1ab54b6-38d1-41b6-79ae-d437e0e8f3ea.png)


## \<allcaps\>
すべて大文字にします。uppercaseと同じです。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<allcaps>VirtualCast")
```

##### 結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/b1ab54b6-38d1-41b6-79ae-d437e0e8f3ea.png)




## \<smallcaps\>
すべて大文字にしますが、小文字だった文字を小さく表示します。ちょっとおしゃれなロゴにたまにあるような表現になります。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<smallcaps>VirtualCast")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/aedc3653-2754-2442-845c-32f43880df8f.png)



## \<margin\>
余白を設定します。marginだけでは左右に設定されますが、左だけ、右だけ設定した場合はそれぞれmargin-left、margin-rightを使います。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text (1)","<align=left>"..
"hogehogehogehoeg<br>"..
"<margin=1>hogehogehogehoge</margin><br>"..
"<margin-left=1>hogehogehogehoge</margin><br>"..
"<margin-right=1>hogehogehogehoge</margin>")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/d0d5ddf8-fd95-a98d-0136-6c6fd3147937.png)



## \<mark\>
マーカーをつけます。色はカラーコードで指定します。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<mark=#00ff0022>楽しいを居場所にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/7a9c90ff-bdc0-88d0-3839-56038d96e8b4.png)

マーカーは文字の上に乗ってしまうので、必ずアルファ値を設定してください。さもなくばただの長方形になってしまいます。


## \<mspace\>
文字固有の空白を上書きして、等幅フォントにします。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<align=left><size=1>liiiiiiiii<br>wmwmwmwmwm<br>"..
"<mspace=1em>liiiiiiiii<br>wmwmwmwmwm<br>")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/858b367d-c62d-3bad-383b-38d27204220d.png)

最初の２行では横幅の小さいスリムなliiiii..という文字列とwmwmwm...という文字列は文字数は同じなのにもかかわらず、全体の幅は倍以上違いますが、３行目からの２行は全体の幅が一致しています。
一見ピンとこないかもしれませんが、 **特に情報を出力する系統のインターフェースで重宝します** 。どんな単語を出してもレイアウトが崩れなくなるため文字主体のデザインや、変化を比較したい場合に力を発揮します。この記事を書くきっかけになったのも某VCI作者が等幅フォントがあればなぁ～って言っていた際に教えたのがきっかけです。情報パネルはよく使うので自分もたまに使います。


## \<noparse\>
このタグで囲われた領域のタグを無視します。 **インジェクションされたくない場合** などにどうぞ。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<color=#ff3333>吾輩は<noparse><color=yellow>猫<br>である。名前はまだない。</noparse>")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/2c4a48f6-5499-7489-463c-32de1efc3165.png)

あくまで囲われた領域のタグを無視なので、それ以前に効果を発揮しているタグは有効です。brだけはきいてしまうようです。

## \<nobr\>
改行の禁止領域を指定する。途中で折れてほしくない単語などがある際に使用する。折り返しを有効にしていないと違いはわからないと思われます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text (1)","<align=left>メタバースプラットフォーム"..
"Virtual Castはみゅみゅ教授によって2010年代くらいになんとなく開発されたアプリケーションがもとになってるような気がする。")

vci.assets.SetText("Text (1)","<align=left>メタバースプラットフォーム"..
"<nobr>Virtual Cast</nobr>はみゅみゅ教授によって2010年代くらいになんとなく開発されたアプリケーションがもとになってるような気がする。")
```

##### 結果
何もなし
| 何もなし | nobr指定 |
|:------------:|:------------:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/7a09473d-fd6d-199d-f21e-95610eb8a4aa.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/ab594c08-0867-e2f2-2577-bb02b28aa2e3.png)|

"Virtual Cast"が途中で改行していないのがわかります


## \<size\>
文字のサイズを指定します。指定できるのはemとパーセンテージ。相対サイズなので、負の値も入れられます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<size=1>VR<size=2>VR<size=3>VR<br><size=-3>VR<size=-2>VR<br><size=10%>VR<size=30%>VR")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/1eba1083-c680-28b0-06c1-b25edc441459.png)



## \<space\>
スペースを入れます。スペースの量は調整できます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","フレ<space=4>ンダ")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/ae3b46b8-fb41-5670-dbd8-aee76c218fd8.png)

一見使いどころがわかりにくいかもしれません。使ったことはありませんが、スペースサイズをプログラマブルに変化させられるので何かに使えるかもしれません。


## \<sprite\>
絵文字などの拡張文字を入力します。文字コードの法則性はちょっとわからなかったです。色を混ぜることもできます。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<sprite=3000>/<sprite=3000　color=#55FF55FF>")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/adf0abcf-4fa4-f8fa-afc3-28b50d54e91e.png)


こんなの使えないだろうと思って使ってみたら使えてびっくりしました。どうやらコメントの絵文字対応で入れたようです。

## \<style\>
カスタムスタイルを設定します。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<style=H1>hoge</style><br>"..
"<style=H2>hoge</style><br><style=C2>hoge</style><br>hoge")

```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/22d3bcdd-eaf6-f6af-d6fa-3dfe26994781.png)

こんなの使えないだろうと思って使ってみたら使えてびっくりしましたその２。ただしこちらはプリセットしかなく、あまり使いどころはなさそう。
本来、このスタイルのほうを開発者がいくつか作って全体でスタイルを使いまわすのが本来の使い方なので、（たぶん）スタイルを作れない現状（未検証）では有効に作用しないだろうと思う。

## \<sub\>
下付き文字を作ります

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","H<sub>2</sub>O")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/13b2c103-7bd9-1c10-4ddc-bb01ff32a021.png)



## \<sup\>
上付き文字を作ります

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","e<sup>iθ</sup>=cos(θ)+ιsin(θ)")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/fdd115f4-3375-eb6f-8532-47cb3ebb421c.png)



## \<voffset\>
垂直方向の位置を調整します。ピクセル単位かフォント単位で指定できるようですが、例によってピクセル単位は無視されるようです

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<size=1>ab<voffset=1>cd<voffset=-1>ef<br>ghi")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/1d481dc8-586c-d383-f727-b54314c167ee.png)

ちなみに、次の行はかなり間が空くみたいです。


## \<width\>
ドキュメントにはテキストの幅と書かれていてなんのことやらちょっとわかりにくいですが、テキストエリアの幅を設定します。したがってこれは折り返しを有効にしていないとわかりにくいです。（無効の場合も変わるが、単純に平行移動しているようにしか見えない）

##### 使用例
```lua:main.lua
vci.assets.SetText("Text (1)","楽しいを居場所にしよう<br>"..
"<width=3>楽しいを居場所にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/1e1de4bc-1f25-dc3e-58b9-a2c8bc5b0563.png)


## \<rotate\>
文字を回転させます。数値の単位は度です。

##### 使用例
```lua:main.lua
vci.assets.SetText("Text","<rotate=180>楽しい<rotate=0>を"..
"<rotate=-20>居<rotate=0>場<rotate=20>所<rotate=0>にしよう")
```

##### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/59ac4558-35bd-4775-0d89-b280bc9e6587.png)

回転中心はその文字毎の中心になりますので、全体を回転させたい場合はオブジェクトを回転させましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/3016be7c-a1df-a91d-3fa7-68d4fe94c679.png)



## おわりに
以上のように、unity上でいじれるプロパティのかなりの部分をタグでいじることができます。これとluaスクリプトとを合わせることでいろいろなことができるようになるのではないでしょうか。

できれば数式を入れれるようにしてほしいですね。一番主には教育で使われてるわけですから、TeX対応してほしいものです。TextMeshPro自体がdeplicatedにもうすぐなるそうなので、スクロールなどと合わせて将来的に対応するとよいですね。



# 参考
https://docs.unity3d.com/Packages/com.unity.textmeshpro@4.0/manual/TMPObjectUIText.html
http://digitalnativestudios.com/textmeshpro/docs/rich-text/
https://www.midnightunity.net/textmeshpro-richtext-tags/
https://docs.unity3d.com/Packages/com.unity.textmeshpro@4.0/manual/RichTextAlignment.html
https://virtualcast.jp/blog/2019/10/emoji/


[^1]:埋め込み型は特に気にする必要はないですが(例:br)、領域指定型はちょっと注意が必要です。領域指定型は効果を発揮するのは開始タグの直後からなので終了タグがなくても動いてしまいます。そのためしばしば終了のタグを省略されるのですが、複数個使ったりすると影響範囲が被ってしまうことが多いです。バグの温床なので使う際は影響範囲がどこなのかしっかり把握して使いましょう。
[^2]:よくあるタグでは属性名を別に書いてそこに引用符をつけて入れますが、TextMeshPro（とluaスクリプト）では属性名がないことが多く、また、引用符をつけてしまうと文字列が壊れてしまうので注意。（エスケープ使えば無理やりできますが特に意味はないので素直に慣習に従いましょう）