はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】TextMeshProUGUIで描画したテキストに座布団(テキストの後ろの背景)をつけてみる

はじめに

今回はTextMeshProUGUIで描画したテキストに座布団(テキストの後ろの背景)をつけてみるという記事になります。

テキストの座布団

TextMeshProならUnityEngine.Boundsを利用することで割と簡単に実現できるのですが、TextMeshProUGUIではその手法が上手く使えずどうすれば良いかなーと考えていました。
TextMeshPro に背景色(Background-color)を付けてみる | Unity+AssetStoreおすすめ情報

またContent Size FitterHorizontal Layout Groupの合わせ技でも似たことができますが、テキストが改行されたときの挙動が私の目的とするものと違ったので見送ってます。
見出しを表示する。uGUI。文字の長さに合わせて背景のサイズ変更したい。 - Qiita


ひとまず以下の手法で実現することができたので書き残しておきますが、実装のコレジャナイ感があり他に良さそうな手法を見つけたら直そうかなという感じではあります。

  • TextMeshProUGUIのテキストのサイズ(preferredWidthpreferredHeight)にRectTransform.sizeDeltaを合わせる
  • 背景のImageを予め設定しておいたPaddingを考慮しながら、RectTransform.sizeDeltaを合わせる

ゲームオブジェクトの関係

Image
  |-----TextMeshProUGUI
ゲームオブジェクトの関係性

コード

TextMeshProUGUIコンポーネントがアタッチされているゲームオブジェクトに以下のコンポーネントをアタッチします。

[RequireComponent(typeof(TextMeshProUGUI))]
public class TextMeshProUGUIZabuton : MonoBehaviour
{
    private const float Tolerance = 0.00001f;
        
    [SerializeField] private Image image;
    [SerializeField] private float paddingWidth;
    [SerializeField] private float paddingHeight;

    private TextMeshProUGUI _tmp;
    private float _preWidth;
    private float _preHeight;

    private void Start()
    {
        _tmp = GetComponent<TextMeshProUGUI>();
    }

    private void Update()
    {
        if (Math.Abs(_preWidth - _tmp.preferredWidth) < Tolerance && Math.Abs(_preHeight - _tmp.preferredHeight) < Tolerance) return;

        UpdateTMProUGUISizeDelta();
        UpdateImageSizeDelta();
    }

    /// <summary>
    /// RectTransform.sizeDeltaをテキストにぴっちりさせる
    /// </summary>
    private void UpdateTMProUGUISizeDelta()
    {
        _preWidth = _tmp.preferredWidth;
        _preHeight = _tmp.preferredHeight;
        _tmp.rectTransform.sizeDelta = new Vector2(_preWidth, _preHeight);
    }
        
    /// <summary>
    /// 背景のImageのRectTransform.sizeDeltaを指定したパディングで更新
    /// </summary>
    private void UpdateImageSizeDelta()
    {
        if (_preHeight == 0 || _preWidth == 0)
        {
            image.rectTransform.sizeDelta = Vector2.zero;
            return;
        }

        image.rectTransform.sizeDelta = new Vector2(_preWidth + paddingWidth, _preHeight + paddingHeight);
    }
}

あとは背景となるImageをインスペクターから設定すればOKです。

ひとこと

調べたところWrappingEnabledになっていると、新しく文字を追加したときに1フレームだけpaddingHeightの値が大きくなってしまうようです。

一応WrappingDisabledにしたら治ったので共有しておきます。