Unity TextMeshPro 實現文本逐字淡出效果
- 前言
- 項目
- 思路
- 場景布置
- 代碼編寫
前言
在處理角色對話時經常會用到一些文本動畫,正好記錄一下。使用 TextMeshPro,我們可以直接操作文本的頂點數據,實現諸如漸變、動畫等效果,為游戲界面和應用程序增添動感。
項目
思路
實現文字緩慢出現的關鍵在于:
-
初始狀態設置
在文字顯示前,將所有字符的頂點顏色透明度(Alpha)設為 0,確保文本初始完全不可見。 -
逐字符漸顯
利用協程逐個為每個字符開啟漸變效果,緩慢將透明度從 0 過渡到 255。這里需要注意:
- 避免在漸顯過程中頻繁調用 ForceMeshUpdate(),因為每次調用都會重置網格數據,可能導致其他字符狀態被覆蓋。
- 預先緩存目標字符的材質索引、頂點索引和顏色數組,確保只修改目標字符的數據。
- 網格數據同步
每次修改完頂點顏色后,需要將顏色數組重新應用到網格上,并調用 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();}
}