Unity TMP可控角度多色渐变文字

Unity TMP可控角度多色渐变文字

前言

遇到一个需要可以控制角度和支持多色渐变文字的需求,记录一下。

在 UI 设计中,渐变色文字是一种常见且非常吸引眼球的视觉效果。

然而,Unity 的 TextMeshPro(简称 TMP)原生只支持单色或简单的顶点渐变。

如果我们想要实现 多种颜色、可控比例、可调整方向(斜率) 的整体渐变效果,就需要自己扩展一段脚本。

项目

首先我们需要一个基本的 Unity UI 场景。

版本推荐:Unity 2021 LTS 或更高版本,并导入 TextMeshPro(默认已内置)。

场景布置

创建TextMeshPro - Text的文本

代码编写

计算文本所有顶点的二维坐标投影,并根据自定义颜色点(ColorStop)的分布与角度插值出最终颜色。

cs 复制代码
using UnityEngine;
using TMPro;

[ExecuteAlways]
[RequireComponent(typeof(TextMeshProUGUI))]
public class TMP_TextGradientVisualizer : MonoBehaviour
{
    [Header("渐变控制")]
    public Gradient gradient = new Gradient();   // 使用Unity自带的Gradient编辑器
    [Range(0f, 360f)]
    public float gradientAngle = 0f;             // 渐变方向(角度控制)

    private TextMeshProUGUI _tmp;
    private bool _needsUpdate = false;

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

    void OnEnable()
    {
        if (_tmp == null) _tmp = GetComponent<TextMeshProUGUI>();
        TMPro_EventManager.TEXT_CHANGED_EVENT.Add(OnTextChanged);
        ApplyGradient();
    }

    void OnDisable()
    {
        TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(OnTextChanged);
    }

    void LateUpdate()
    {
        if (_needsUpdate)
        {
            ApplyGradient();
            _needsUpdate = false;
        }
    }

    void OnTextChanged(Object obj)
    {
        if (obj == _tmp)
            _needsUpdate = true;
    }

    public void ApplyGradient()
    {
        if (_tmp == null || gradient == null)
            return;

        _tmp.ForceMeshUpdate(true, true);
        TMP_TextInfo textInfo = _tmp.textInfo;
        if (textInfo == null || textInfo.characterCount == 0) return;

        // 找出整体边界
        Vector2 min = new Vector2(float.MaxValue, float.MaxValue);
        Vector2 max = new Vector2(float.MinValue, float.MinValue);
        for (int i = 0; i < textInfo.characterCount; i++)
        {
            TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
            if (!charInfo.isVisible) continue;
            Vector3 bl = charInfo.bottomLeft;
            Vector3 tr = charInfo.topRight;
            min.x = Mathf.Min(min.x, bl.x);
            min.y = Mathf.Min(min.y, bl.y);
            max.x = Mathf.Max(max.x, tr.x);
            max.y = Mathf.Max(max.y, tr.y);
        }
        if (min.x == float.MaxValue) return;

        // 根据角度计算方向
        float rad = gradientAngle * Mathf.Deg2Rad;
        Vector2 dir = new Vector2(Mathf.Cos(rad), Mathf.Sin(rad)).normalized;

        // 计算投影范围
        float minProj = float.MaxValue, maxProj = float.MinValue;
        Vector2[] corners = new Vector2[]
        {
            new Vector2(min.x, min.y),
            new Vector2(max.x, min.y),
            new Vector2(min.x, max.y),
            new Vector2(max.x, max.y)
        };
        foreach (var c in corners)
        {
            float proj = Vector2.Dot(c, dir);
            minProj = Mathf.Min(minProj, proj);
            maxProj = Mathf.Max(maxProj, proj);
        }

        // 遍历所有网格顶点并设置颜色
        for (int i = 0; i < textInfo.meshInfo.Length; i++)
        {
            TMP_MeshInfo meshInfo = textInfo.meshInfo[i];
            if (meshInfo.vertices == null) continue;

            Color32[] colors = meshInfo.colors32;
            Vector3[] vertices = meshInfo.vertices;

            for (int j = 0; j < vertices.Length; j++)
            {
                Vector2 v = vertices[j];
                float proj = Vector2.Dot(v, dir);
                float t = Mathf.InverseLerp(minProj, maxProj, proj);
                colors[j] = gradient.Evaluate(t);
            }
        }

        _tmp.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
    }

#if UNITY_EDITOR
    void OnValidate()
    {
        if (_tmp == null) _tmp = GetComponent<TextMeshProUGUI>();
        if (!Application.isPlaying)
        {
            UnityEditor.EditorApplication.delayCall += () =>
            {
                if (this != null && _tmp != null)
                    ApplyGradient();
            };
        }
    }
#endif
}

原理解析

  1. 顶点投影计算

    每个 TMP 顶点都是二维平面坐标,我们根据指定角度构造一个单位方向向量 dir。

    然后用点积计算该顶点在该方向上的投影值,从而确定它处于渐变线的哪一段。

  2. 归一化插值

    通过 Mathf.InverseLerp(minProj, maxProj, proj) 把投影值映射到 0~1。

  3. 颜色插值

    按顺序遍历 colorStops,在相邻两个节点之间进行线性插值 Color.Lerp(a, b, t)。

  4. 全局更新网格颜色

    修改 meshInfo.colors32 后,用 _tmp.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32) 更新到屏幕。

添加并设置脚本

挂载脚本并设置

  • Gradient Angle:控制渐变的方向(0°水平,90°垂直,其它值为斜向)
  • Color Stops:控制渐变颜色节点。


总结

之后可以添加GradientPreset资源,统一 UI 风格。

相关推荐
霜绛9 小时前
Unity:UGUI笔记(一)——三大基础控件、组合控件
笔记·学习·unity·游戏引擎
小趴菜822714 小时前
Android中加载unity aar包实现方案
android·unity·游戏引擎
今夕资源网1 天前
牛童三国单机游戏Unity源码 免费开源
游戏·unity·单机游戏·游戏源码·unity源码·unity游戏
future_studio1 天前
聊聊 Unity(小白专享、C# 小程序 之 图片播放器)
unity·小程序·c#
ellis19702 天前
toLua[七] Examples 06_LuaCoroutine2分析
unity
L X..2 天前
Unity 光照贴图异常修复笔记
unity·c#·游戏引擎
Xeon_CC2 天前
打开多个Unity编辑器时使用Visual Studio调试,弹出选择Unity实例窗口,但是没有实例
unity·编辑器·visual studio·调试·unity 调试
小L~~~2 天前
2025吉比特-游戏引擎开发-一面复盘
数据结构·算法·游戏引擎
云卓SKYDROID2 天前
无人机中继器模式技术对比
人工智能·游戏引擎·php·无人机·cocos2d·高科技·云卓科技