Unity TextMeshPro 实现文本逐字淡出效果

发布于:2025-04-04 ⋅ 阅读:(30) ⋅ 点赞:(0)

Unity TextMeshPro 实现文本逐字淡出效果

前言

在处理角色对话时经常会用到一些文本动画,正好记录一下。使用 TextMeshPro,我们可以直接操作文本的顶点数据,实现诸如渐变、动画等效果,为游戏界面和应用程序增添动感。
逐字淡出效果

项目

思路

实现文字缓慢出现的关键在于:

  1. 初始状态设置
    在文字显示前,将所有字符的顶点颜色透明度(Alpha)设为 0,确保文本初始完全不可见。

  2. 逐字符渐显
    利用协程逐个为每个字符开启渐变效果,缓慢将透明度从 0 过渡到 255。这里需要注意:

  • 避免在渐显过程中频繁调用 ForceMeshUpdate(),因为每次调用都会重置网格数据,可能导致其他字符状态被覆盖。
  • 预先缓存目标字符的材质索引、顶点索引和颜色数组,确保只修改目标字符的数据。
  1. 网格数据同步
    每次修改完顶点颜色后,需要将颜色数组重新应用到网格上,并调用 UpdateVertexData() 来刷新显示。

场景布置

场景截图

代码编写

using UnityEngine;
using TMPro;
using System.Collections;
using UnityEngine.PlayerLoop;

public class TextFadeIn : MonoBehaviour
{
    public float fadeDuration = 0.5f; // 每个字符的渐变时间
    public float interval = 0.1f;     // 字符之间的间隔时间

    public TMP_Text textComponent;
    public string originalText;


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ShowTextAnim("The key is not to re enable automatic mesh generation after modifying the vertex color. Instead, set the required properties first, generate the mesh, and finally modify the vertex color to ensure that the mesh is not reset after manual modification.");
        }
    }

    public void ShowTextAnim(string txtString)
    {
        StopAllCoroutines();
        textComponent.text = "";
        originalText = txtString;

        StartCoroutine(DelayedStart());
    }

    IEnumerator DelayedStart()
    {
        // 先设置好文本和属性,启用 word wrapping(如果需要)
        textComponent.enableWordWrapping = true;
        textComponent.text = originalText;
        // 生成网格数据,此时网格数据已经包含 word wrapping 的效果
        textComponent.ForceMeshUpdate();

        // 获取最新的文本信息
        TMP_TextInfo textInfo = textComponent.textInfo;

        // 将所有可见字符的顶点颜色的 alpha 设置为 0(透明)
        for (int i = 0; i < textInfo.characterCount; i++)
        {
            if (!textInfo.characterInfo[i].isVisible)
                continue;

            int materialIndex = textInfo.characterInfo[i].materialReferenceIndex;
            int vertexIndex = textInfo.characterInfo[i].vertexIndex;
            Color32[] vertexColors = textInfo.meshInfo[materialIndex].colors32;
            for (int j = 0; j < 4; j++)
            {
                vertexColors[vertexIndex + j].a = 0;
            }
        }

        // 应用顶点颜色更改到网格
        for (int i = 0; i < textInfo.meshInfo.Length; i++)
        {
            textInfo.meshInfo[i].mesh.colors32 = textInfo.meshInfo[i].colors32;
        }
        textComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);

        // 等待一帧确保更改已生效
        yield return null;

        // 开始字符渐入效果
        StartCoroutine(ShowText());
    }

    IEnumerator ShowText()
    {
        TMP_TextInfo textInfo = textComponent.textInfo;
        int totalCharacters = textInfo.characterCount;

        // 逐个启动字符渐显协程(顺序进行)
        for (int i = 0; i < totalCharacters; i++)
        {
            if (textInfo.characterInfo[i].isVisible)
            {
                // 等待当前字符渐显完成后再处理下一个字符
                yield return StartCoroutine(FadeCharacter(i));
                yield return new WaitForSeconds(interval);
            }
        }
    }

    IEnumerator FadeCharacter(int characterIndex)
    {
        TMP_TextInfo textInfo = textComponent.textInfo;

        if (characterIndex >= textInfo.characterCount || !textInfo.characterInfo[characterIndex].isVisible)
            yield break;

        // 缓存目标字符的相关信息
        TMP_CharacterInfo charInfo = textInfo.characterInfo[characterIndex];
        int materialIndex = charInfo.materialReferenceIndex;
        int vertexIndex = charInfo.vertexIndex;
        Color32[] vertexColors = textInfo.meshInfo[materialIndex].colors32;

        float elapsedTime = 0f;
        while (elapsedTime < fadeDuration)
        {
            elapsedTime += Time.deltaTime;
            float alpha = Mathf.Clamp01(elapsedTime / fadeDuration);
            byte alphaByte = (byte)(alpha * 255);

            // 仅更新目标字符的顶点颜色
            for (int j = 0; j < 4; j++)
            {
                vertexColors[vertexIndex + j].a = alphaByte;
            }

            // 将更新后的颜色数组直接应用到对应网格
            textInfo.meshInfo[materialIndex].mesh.colors32 = vertexColors;
            textComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);

            yield return null;
        }
    }

    private void OnDisable()
    {
        StopAllCoroutines();
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到