当前位置: 首页 > news >正文

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

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();
    }
}

相关文章:

  • 5.02 WPF的 Combox、ListBox,slider、ProgressBar使用
  • C语言--插入排序
  • Kafka+Zookeeper从docker部署到spring boot使用完整教程
  • Vue:生命周期
  • 2025年山东水利水电 C 证考试精选题库
  • android adb 查看设备传感器
  • JS实现动态点图酷炫效果
  • git命令简陋版本
  • MySQL 复制与主从架构(Master-Slave)
  • Day48 | 657. 机器人能否返回原点、31. 下一个排列、463. 岛屿的周长、1356. 根据数字二进制下 1 的数目排序
  • 嵌入式系统简介
  • PH热榜 | 2025-03-31
  • MTU | 检测 / 设置 / 相关问题解析
  • 【JavaEE进阶】MyBatis(4)-完善图书管理系统
  • ros2--xacro
  • uniapp中如何用iconfont来管理图标集成到我们开发的项目中
  • 基于AT89C52单片机的轮胎压力监测系统
  • 学习threejs,使用THREE.ImageUtils.loadTexture加载纹理贴图
  • C++11:包装器(适配器模式)
  • TCP/IP协议的应用层与传输层
  • 小学生做网站/百度销售平台
  • 做网站推广有前景吗/360手机优化大师下载
  • 北京市东城区住房城乡建设委网站/百度首页官网
  • 黄冈网站建设设计/b站推广软件
  • 为什么上传网站模板网站上没有文字和图片/成人短期技能培训
  • 简历上作品展示网站链接怎么做/培训行业seo整站优化