うどんてっくメモ

技術的なメモをまったりと

【Unity】TextMeshProのアウトラインの太さをランタイムで変更する

はじめに

本記事で紹介および検証を行なっているツール、ライブラリのバージョンは次の通りです。

  • Unity 2022.3.16f1
  • com.unity.textmeshpro@3.0.6

バージョンによっては挙動に差異がある場合もありますので、ご了承ください。

TextMeshProのアウトライン

TexeMeshProでアウトラインを表現する際にはマテリアルを調整することになります。 詳しくは以前記事を書いてるのでそちらを参照してください。

myudon.hatenablog.com

マテリアルのアウトラインの太さを制御しているのが「Thickness」と「Dilate」になります。 これはシェーダーのプロパティとしては「_OutlineWidth」と「_FaceDilate」として用意されています。

下記がシェーダーの引用です。

Shader "TextMeshPro/Distance Field" {

Properties {
    _FaceTex            ("Face Texture", 2D) = "white" {}
    _FaceUVSpeedX       ("Face UV Speed X", Range(-5, 5)) = 0.0
    _FaceUVSpeedY       ("Face UV Speed Y", Range(-5, 5)) = 0.0
    [HDR]_FaceColor     ("Face Color", Color) = (1,1,1,1)
    _FaceDilate         ("Face Dilate", Range(-1,1)) = 0

    [HDR]_OutlineColor  ("Outline Color", Color) = (0,0,0,1)
    _OutlineTex         ("Outline Texture", 2D) = "white" {}
    _OutlineUVSpeedX    ("Outline UV Speed X", Range(-5, 5)) = 0.0
    _OutlineUVSpeedY    ("Outline UV Speed Y", Range(-5, 5)) = 0.0
    _OutlineWidth       ("Outline Thickness", Range(0, 1)) = 0
    _OutlineSoftness    ("Outline Softness", Range(0,1)) = 0
//...(以下省略

アウトラインの大きさを変える

マテリアルの「_OutlineWidth」と「_FaceDilate」をランタイムで更新することで、太さを調整することが可能です。

private void SetOutlineMaterialPropertySample(Material material, float outlineWidth, float dilate)
{
    material.SetFloat("_OutlineWidth", outlineWidth);
    material.SetFloat("_FaceDilate", dilate);
}

TextMeshProUGUIから装飾に該当するマテリアルを参照する際は、fontSharedMaterialを参照します。

private void SetOutlineMaterialPropertySample(TextMeshProUGUI text, float outlineWidth, float dilate)
{
    var material = text.fontSharedMaterial;
    material.SetFloat("_OutlineWidth", outlineWidth);
    material.SetFloat("_FaceDilate", dilate);
}

アウトラインの検証機能を実装してみる

マテリアルを動的に変更するアプローチをもとに、アウトラインの検証を行う機能を作ってみます。 先にスクリプトの全容だけ乗せます。

using TMPro;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 適当なアウトラインの太さ調整を行なってみるサンプルUI
/// </summary>
public class FontOutlineController : MonoBehaviour
{
    [SerializeField] 
    private Slider _fontSlider;
    
    [SerializeField] 
    private TextMeshProUGUI[] _sampleTexts;

    private void Awake()
    {
        _fontSlider.onValueChanged.AddListener(SetSampleTextsOutlineMaterialProperty);
    }

    /// <summary>
    /// 適当に指定したサンプルのテキストにアウトライン設定を行う
    /// </summary>
    private void SetSampleTextsOutlineMaterialProperty(float outlinePixel)
    {
        foreach (var text in _sampleTexts)
        {
            SetOutlineMaterialProperty(text, outlinePixel);
        }
    }
    
    /// <summary>
    /// アウトラインをピクセル単位で指定
    /// </summary>
    private void SetOutlineMaterialProperty(TextMeshProUGUI text, float outlinePixel)
    {
        var fontSize = text.fontSize;
        var offset = GetOutlineThicknessOffset(text, outlinePixel, fontSize);
        var material = text.fontSharedMaterial;
        material.SetFloat("_OutlineWidth", offset);
        material.SetFloat("_FaceDilate", offset);
    }

    /// <summary>
    /// TMPのアウトラインに基づく指定ピクセルサイズの太さのための設定値計算
    /// </summary>
    /// <returns></returns>
    private float GetOutlineThicknessOffset(TextMeshProUGUI text, float pixelSize, float fontSize)
    {
        var fontAsset = text.font;
        var padding = fontAsset.creationSettings.padding;
        var samplingSize = fontAsset.creationSettings.pointSize;
        // マテリアルの値(px) = 実現したいアウトラインの太さ / 文字のフォントサイズ * SamplingSize / Padding / 2
        return pixelSize / fontSize * samplingSize / padding / 2;
    }
}

これを適当なUIに当てはめて実行できるようにしてみます。 今回は検証として異なるマテリアルを設定した文字をいくつか並べ、SerializeFieldからアタッチし、適用させます。

このように、アウトラインの太さを実行中にピクセル単位で調整できる機能は、一番見栄えのいい状態や表示崩れが起きてしまう値の検証することができます。

まとめ

アウトラインは実際にランタイムで確認してみないとデザインの調整がしづらいものですが、マテリアルを動的に調整する機能を用意することでそういった調整のサポートをすることが可能です。

今回はアウトラインに関係する値だけ調整しましたが、他の要素の調整にも活用可能なのでぜひご活用ください。