You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Unity TextMeshPro添加带自定义描边的自定义<link>标签?

实现TextMeshPro自定义发光描边标签的方案

要解决修改材质影响所有文本的问题,核心是为标签内的文本单独创建材质实例,而非直接修改原材质。以下是具体实现步骤:

1. 确认基础条件

确保你的TextMeshPro使用的Shader支持描边与发光效果,比如内置的TextMeshPro/Distance Field或TextMeshPro/Mobile/Distance Field,这类Shader默认带有描边、自发光参数。

2. 编写自定义标签处理脚本

创建一个C#脚本,挂载在带有TextMeshPro组件的对象上,实现标签解析与材质实例化逻辑:

using UnityEngine;
using TMPro;

[RequireComponent(typeof(TMP_Text))]
public class CustomOutlineTagHandler : MonoBehaviour
{
    private TMP_Text tmpText;
    // 存储材质实例,用于后续销毁避免内存泄漏
    private Material outlineMaterialInstance;

    void Awake()
    {
        tmpText = GetComponent<TMP_Text>();
        // 订阅文本更新事件,每次文本刷新时重新处理标签
        tmpText.textChanged.AddListener(OnTextChanged);
    }

    void OnTextChanged()
    {
        // 销毁旧材质实例(如果存在)
        if (outlineMaterialInstance != null)
        {
            Destroy(outlineMaterialInstance);
            outlineMaterialInstance = null;
        }

        TMP_TextInfo textInfo = tmpText.textInfo;
        bool inOutlineTag = false;
        int startIndex = -1;

        // 遍历所有字符,识别标签范围
        for (int i = 0; i < textInfo.characterCount; i++)
        {
            TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
            if (!charInfo.isVisible) continue;

            if (charInfo.textElementType == TextElementType.Tag)
            {
                string tagName = tmpText.text.Substring(charInfo.characterIndex + 1, charInfo.characterLength - 2);
                if (tagName == "customoutline")
                {
                    if (!inOutlineTag)
                    {
                        inOutlineTag = true;
                        startIndex = i + 1; // 标签后的第一个有效字符
                    }
                    else
                    {
                        inOutlineTag = false;
                        // 处理标签范围内的字符
                        ApplyOutlineToRange(startIndex, i - 1);
                        startIndex = -1;
                    }
                }
            }
        }

        // 处理未闭合的标签(可选逻辑)
        if (inOutlineTag && startIndex != -1)
        {
            ApplyOutlineToRange(startIndex, textInfo.characterCount - 1);
        }
    }

    void ApplyOutlineToRange(int startIndex, int endIndex)
    {
        TMP_TextInfo textInfo = tmpText.textInfo;

        // 创建原材质的实例副本
        if (outlineMaterialInstance == null)
        {
            outlineMaterialInstance = new Material(tmpText.fontMaterial);
            // 自定义描边与发光参数
            outlineMaterialInstance.SetFloat(ShaderUtilities.ID_OutlineWidth, 0.1f);
            outlineMaterialInstance.SetColor(ShaderUtilities.ID_OutlineColor, Color.cyan);
            outlineMaterialInstance.SetColor(ShaderUtilities.ID_GlowColor, Color.cyan);
            outlineMaterialInstance.SetFloat(ShaderUtilities.ID_GlowPower, 2f);
        }

        // 将材质实例添加到TextMeshPro的材质列表(如果未添加)
        int outlineMaterialIndex = tmpText.materials.IndexOf(outlineMaterialInstance);
        if (outlineMaterialIndex == -1)
        {
            outlineMaterialIndex = tmpText.materials.Count;
            tmpText.materials.Add(outlineMaterialInstance);
        }

        // 为范围内的字符分配材质实例
        for (int i = startIndex; i <= endIndex; i++)
        {
            TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
            if (!charInfo.isVisible) continue;

            int meshIndex = charInfo.materialReferenceIndex;
            TMP_MeshInfo meshInfo = textInfo.meshInfo[meshIndex];
            int localCharIndex = i - meshInfo.firstCharacterIndex;

            // 更新字符的材质索引
            meshInfo.characterInfo[localCharIndex].materialReferenceIndex = outlineMaterialIndex;
            meshInfo.mesh.markDynamic = true;
            textInfo.meshInfo[meshIndex] = meshInfo;
        }

        // 刷新TextMeshPro显示
        tmpText.UpdateVertexData();
    }

    void OnDestroy()
    {
        // 销毁材质实例,避免内存泄漏
        if (outlineMaterialInstance != null)
        {
            Destroy(outlineMaterialInstance);
        }
    }
}

3. 使用自定义标签

在TextMeshPro的文本内容中直接使用<customoutline>标签即可:

this is a <customoutline>test</customoutline> with glowing outline.

关键说明

  • 材质隔离:通过new Material(tmpText.fontMaterial)创建原材质的副本,修改参数只会影响当前文本的标签段,不会波及其他使用原材质的文本。
  • 内存管理:在文本更新和脚本销毁时主动销毁旧材质实例,避免内存泄漏。
  • 效果定制:可根据需求调整OutlineWidth、OutlineColor、GlowColor、GlowPower等参数,或替换为支持更强发光效果的自定义Shader。

内容的提问来源于stack exchange,提问作者Death Pad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 20:09:24