using System.Collections; using TMPro; using UnityEngine; [RequireComponent(typeof(TextMeshProUGUI))] public class TextMove : MonoBehaviour { [SerializeField] private float _interval = 0.25f; [SerializeField] private float _moveTime = 0.5f; [SerializeField] private Vector2 _moveOffset = new Vector2(0, 1f); private TMP_MeshInfo[] cachedMeshInfo; private IEnumerator Start() { var component = GetComponent<TMP_Text>(); component.ForceMeshUpdate(); var textInfo = component.textInfo; var cc = textInfo.characterCount; cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); while (true) { for (var characterIndex = 0; characterIndex < cc; characterIndex++) { var charInfo = textInfo.characterInfo[characterIndex]; if (charInfo.isVisible) { StartCoroutine(UpdatePosition(characterIndex)); yield return new WaitForSeconds(_interval); } } } } private IEnumerator UpdatePosition(int index) { var component = GetComponent<TMP_Text>(); var textInfo = component.textInfo; var charInfo = textInfo.characterInfo[index]; var materialIndex = charInfo.materialReferenceIndex; var vertexIndex = charInfo.vertexIndex; var offset = Vector3.zero; var current = 0f; while (true) { var t = Mathf.InverseLerp(0, _moveTime, current); t = Mathf.InverseLerp(t >= 0.5f ? 1 : 0, 0.5f, t); Update(t); current += Time.deltaTime; if (current >= _moveTime) { Update(0); yield break; } yield return null; } void Update(float t) { for (var j = 0; j < 2; j++) { offset[j] = Mathf.Lerp(0, _moveOffset[j], t); } var sourceVertices = cachedMeshInfo[materialIndex].vertices; var destinationVertices = textInfo.meshInfo[materialIndex].vertices; for (var i = 0; i < 4; i++) { var meshIndex = vertexIndex + i; destinationVertices[meshIndex] = sourceVertices[meshIndex] + offset; } textInfo.meshInfo[materialIndex].mesh.vertices = destinationVertices; component.UpdateGeometry(textInfo.meshInfo[materialIndex].mesh, materialIndex); } } }