前言
看见很多游戏有很特殊的波浪形文字效果,于是来尝试一下控制TMP文字顶点的方式达到类似效果。
原理
挂载tmp text,在里面随便放入非空格字符。
tmp text组件开放了textInfo接口,也就是GetComponent<TextMeshProUGUI>().textInfo可以获取text字符串信息:
characterInfo[i].vertexIndex用于找到这个字符起始index;meshInfo[TMP_CharacterInfo.materialReferenceIndex]用于获取mesh信息,再进一层meshInfo.vertices即可拿到顶点信息。
我们就是强行修改vertices信息,然后调用TextMeshProUGUI.UpdateVertexData()即可完成text不同字符间的修改。
保存原始字符顶点vertices信息
//tmp组件
TextMeshProUGUI m_txtSelf = null;
//移动周期
float m_moveTotalTime = 1f;
//上下浮动幅度
float m_amplitude = 100;
Vector3[] m_rawVertex;
public void Awake()
{
m_txtSelf = transform.GetComponent<TextMeshProUGUI>();
}
private void GetRawVertex()
{
m_txtSelf.ForceMeshUpdate();
if (m_txtSelf.textInfo.characterCount > 0)
{
TMP_CharacterInfo charInfo = m_txtSelf.textInfo.characterInfo[0];
TMP_MeshInfo meshInfo = m_txtSelf.textInfo.meshInfo[charInfo.materialReferenceIndex];
//创建对象来保存初始值
m_rawVertex = new Vector3[meshInfo.vertices.Length];
for (int i = 0; i < meshInfo.vertices.Length; i++)
{
m_rawVertex[i] = new Vector3(meshInfo.vertices[i].x, meshInfo.vertices[i].y, meshInfo.vertices[i].z);
}
}
}
public void Start()
{
GetRawVertex();
}
所有字符一起移动
用协程每隔一段时间做一次循环运动,用dotween.to附加lamda表达式来完成字符串移动:
public void Start()
{
GetRawVertex();
StartCoroutine(Shake());
}
public IEnumerator Shake()
{
while(true)
{
Tweener tweener = DOTween.To(() => 0f, y =>
{
for (int i = 0; i < m_txtSelf.textInfo.characterCount; i++)
{
// 获取字符信息和MeshInfo
TMP_CharacterInfo currentCharInfo = m_txtSelf.textInfo.characterInfo[i];
TMP_MeshInfo meshInfo = m_txtSelf.textInfo.meshInfo[currentCharInfo.materialReferenceIndex];
int nextVertexIndex = meshInfo.vertices.Length;
if (i < m_txtSelf.textInfo.characterCount - 1)
{
TMP_CharacterInfo nextCharInfo = m_txtSelf.textInfo.characterInfo[i + 1];
nextVertexIndex = nextCharInfo.vertexIndex;
}
// 获取起始顶点索引
int vertexIndex = currentCharInfo.vertexIndex;
// 顶点偏移
for (int j = vertexIndex; j < nextVertexIndex; j++)
{
float yOffset = y;
if (yOffset >= 0 && yOffset <= 1)
{
meshInfo.vertices[j] = m_rawVertex[j] + Mathf.Sin(yOffset * Mathf.PI) * Vector3.up * m_amplitude;
}
}
}
m_txtSelf.UpdateVertexData();
}, 2, m_moveTotalTime);
yield return new WaitForSeconds(m_moveTotalTime);
}
}
效果如下显示:
每个字符规律跳动
就需要在y->yOffset中加入位置参数变量,用以形成类波浪效果:
float yOffset = y - (float)i / m_txtSelf.textInfo.characterCount;
至此即可完成。
全部代码
using System.Collections;
using UnityEngine;
using TMPro;
using DG.Tweening;
public class test : MonoBehaviour
{
//tmp组件
TextMeshProUGUI m_txtSelf = null;
//移动周期
float m_moveTotalTime = 1f;
//上下浮动幅度
float m_amplitude = 100;
Vector3[] m_rawVertex;
public void Awake()
{
m_txtSelf = transform.GetComponent<TextMeshProUGUI>();
}
private void GetRawVertex()
{
m_txtSelf.ForceMeshUpdate();
if (m_txtSelf.textInfo.characterCount > 0)
{
TMP_CharacterInfo charInfo = m_txtSelf.textInfo.characterInfo[0];
TMP_MeshInfo meshInfo = m_txtSelf.textInfo.meshInfo[charInfo.materialReferenceIndex];
//创建对象来保存初始值
m_rawVertex = new Vector3[meshInfo.vertices.Length];
for (int i = 0; i < meshInfo.vertices.Length; i++)
{
m_rawVertex[i] = new Vector3(meshInfo.vertices[i].x, meshInfo.vertices[i].y, meshInfo.vertices[i].z);
}
}
}
public void Start()
{
GetRawVertex();
StartCoroutine(Shake());
}
public IEnumerator Shake()
{
yield return new WaitForSeconds(1);
while (true)
{
Tweener tweener = DOTween.To(() => 0f, y =>
{
for (int i = 0; i < m_txtSelf.textInfo.characterCount; i++)
{
// 获取字符信息和MeshInfo
TMP_CharacterInfo currentCharInfo = m_txtSelf.textInfo.characterInfo[i];
TMP_MeshInfo meshInfo = m_txtSelf.textInfo.meshInfo[currentCharInfo.materialReferenceIndex];
int nextVertexIndex = meshInfo.vertices.Length;
if (i < m_txtSelf.textInfo.characterCount - 1)
{
TMP_CharacterInfo nextCharInfo = m_txtSelf.textInfo.characterInfo[i + 1];
nextVertexIndex = nextCharInfo.vertexIndex;
}
// 获取起始顶点索引
int vertexIndex = currentCharInfo.vertexIndex;
// 顶点偏移
for (int j = vertexIndex; j < nextVertexIndex; j++)
{
float yOffset = y - (float)i / m_txtSelf.textInfo.characterCount;
if (yOffset >= 0 && yOffset <= 1)
{
meshInfo.vertices[j] = m_rawVertex[j] + Mathf.Sin(yOffset * Mathf.PI) * Vector3.up * m_amplitude;
}
}
}
m_txtSelf.UpdateVertexData();
}, 2, m_moveTotalTime);
yield return new WaitForSeconds(m_moveTotalTime);
}
}
}