[unity]TextMeshPro でルビを表示する

TextMeshPro は多彩な表現を可能にしてくれるテキスト UI ですが、ルビ! 日本のおもてなし! にはさすがに対応してくれません。

でも、なんとかしたいのでなんとかすることにしましょう。

自動的に読み仮名を振ってくれるわけではありません。

入力フォーマット

入力時のフォーマットを決めます。タグとかぶらないよう、{} を使うことにしました。
ルビは1文字に対してとは限らず、漢字3文字にたいしてルビ文字5つ……といった場合も考慮できるフォーマットにします。

この<color=red>{数:かず}</color>は、{一般的:いっぱんてき}に「{ナ:na}{ノ:no}」という{単位:たんい}{接頭辞:せっとうじ}{使用:しよう}して{表:あらわ}されます。

さりげなく文字にカラータグ入れてるのは、当然ルビも同じ色になるんだよなぁ?? という状況にも対応するため入れておきました。

GitHub

使い方

  1. SampleScene.unity を開きます
  2. Draw ボタンを押して確認します。
  3. AutoForwardSpeed や IsDrawAtOnce を変更し、再度 Draw ボタンを押して確認します。

コードの書き方については Sample.cs をご覧ください。

特徴(とか、苦労話)

理屈は単純で、「テキスト文字の表示された座標の上に、ルビのテキストフィールドを配置する」だけです。
が、表示サイズ変更したり、1文字ずつ表示するならどのタイミングでルビの表示を行うかなど、そこそこ考える必要のある案件です。

詳しくはコード(Sample.cs, RubyText.cs)に譲るとして、ここではいくつか特徴や苦労話を語ります。

今何文字目を表示しているか?

文章にルビやタグが入っているので、単純に文字数をカウントするだけでは上手くいきません。
当初適当に実装してたら、思ったより面倒の種になったので、見直しました。

Font の AutoSize にも対応

TextMeshPro のいいところの1つに「指定された枠に収まるよう、自動的にフォントサイズを調整する(AutoSize)」というものがあります。

ただこれ、1フレーム描画した後でないとフォントサイズが確定しないようなので、ルビのサイズも当然1フレーム待った後のサイズで算出する必要があります。

また GetTextInfo().characterInfo で取得できる情報は「現在表示している文字のみ」です。
1文字ずつ表示しよう……という場合、AutoSize オンだと1文字表示するごとに文字が小さくなるなど動きとして不自然になってしまいます。そのため、

  • α0の状態で予め全文字表示(SetText)
  • サイズ情報を算出、ルビの再構築
  • SetText("") で内容をクリア。描画開始

という少しまだるっこしい処理をこっそり行っています。
このため、絶えず 1/60 で文章を切り替えたい、亜高速スキップ可能ゲームには向いていないと思います(ないと思いますが)。

ゲームの場合、大抵はフォント固定サイズにし、収まらない文章は削るなどテキスト側で調整することが多いので、正直ここまで対応する必要はないかもしれません。

ルビの字幅、間隔

ルビの字幅、間隔は品質に関わってくる部分なので、なるべく自然に見えるようにしました。

とはいえ、ルビワード(漢字)が行をまたいで表示されるなど、どうしようもないケースもあります。

「単」「位」が行ずれを起こしている

例えばこのようなケースです。(たんい)が(たん)と(い)に分かれることをプログラムでは把握できないので、最初の文字に(たんい)と表示しています。
どうしても自然に見せる必要があれば {単位:たんい} {単:たん}{位:い} にするなどコンテンツ側で調整してください。

文章は一気に全体表示 or 1文字ずつ表示を選択可能

IsDrawAtOnce に✓を入れると一度に全体表示、✓を外すと1文字ずつ表示されます。速度は AutoForwardSpeed で調整します。
いずれの場合も StartAutoForward() が呼ばれてから描画を開始します。

一応動的表示サイズ変更にも対応しているが……

一応 RectSize(W, H)Wrapping は監視しているので動的に値が変わっても動作しますが、私が軽くチェックしただけですので、自信はありません☺

ルビのバッファはキャッシュされる

文章ごとにルビ用の TextMeshPro を作成すると地味に速度ロスとなりそうなので、使われたルビバッファはキャッシュし、次以降の文章にも使われるようになっています。

バッファは足りなくなった時だけ拡張します。拡張した分は次以降にも残すので、1文章だけビックリするくらいルビ数が……といったケースではメモリロスとなります。ご留意ください。

現実的に考えて、ルビなんていいとこ1文章に 20 もないと思う!


2件のコメント

  1. コメント質問します。
    こちらのアセットを利用させて頂く場合、ライセンス区分はどのような形態になるでしょうか。
    ※どこかで表記を見逃していたら申し訳ございません
    上記ご確認頂けると幸いです。宜しくお願い致します。

    1. ライセンスは MIT に沿ったものとさせていただきました。
      (最新のコードに記載しました)

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA