/
TMProWarpText.cs
153 lines (121 loc) · 5.19 KB
/
TMProWarpText.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
using System;
using TMPro;
#if UNITY_EDITOR
#endif
using UnityEngine;
/// <summary> Adapted from TMPro examples and extras provided by Unity </summary>
[ExecuteAlways]
public class TMProWarpText : MonoBehaviour
{
[SerializeField]
private TMP_Text text;
public AnimationCurve vertexCurve;
public float yCurveScaling = 100f;
private bool isForceUpdatingMesh;
private void Reset()
{
text = gameObject.GetComponent<TMP_Text>();
vertexCurve = new AnimationCurve(
new Keyframe(0, 0, 0, 30, 0, 0.01f), new Keyframe(0.5f, 0.25f), new Keyframe(1, 0, -30, 0, 0.01f, 0));
vertexCurve.preWrapMode = WrapMode.Clamp;
vertexCurve.postWrapMode = WrapMode.Clamp;
WarpText();
}
void Awake()
{
if (!text) text = gameObject.GetComponent<TMP_Text>();
#if UNITY_EDITOR
UnityEditor.SceneManagement.PrefabStage.prefabStageOpened += PrefabStageOpened;
#endif
}
private void OnEnable()
{
WarpText();
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ReactToTextChanged);
}
private void OnDisable()
{
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ReactToTextChanged);
text.ForceMeshUpdate();
}
//private void OnDidApplyAnimationProperties()
//{
// WarpText();
//}
#if UNITY_EDITOR
private void OnDestroy()
{
UnityEditor.SceneManagement.PrefabStage.prefabStageOpened -= PrefabStageOpened;
}
private void PrefabStageOpened(UnityEditor.SceneManagement.PrefabStage prefabStage)
{
WarpText();
}
private void OnValidate()
{
WarpText();
}
#endif
private void ReactToTextChanged(UnityEngine.Object obj)
{
TMP_Text tmpText = obj as TMP_Text;
if (tmpText && text && tmpText == text && !isForceUpdatingMesh)
WarpText();
}
/// <summary> Method to curve text along a Unity animation curve. </summary>
private void WarpText()
{
if (!text) return;
isForceUpdatingMesh = true;
Vector3[] vertices;
Matrix4x4 matrix;
text.havePropertiesChanged = true; // Need to force the TextMeshPro Object to be updated.
text.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate.
TMP_TextInfo textInfo = text.textInfo;
if (textInfo == null) return;
int characterCount = textInfo.characterInfo.Length;
if (characterCount == 0 || textInfo.characterCount == 0) return;
float boundsMinX = text.bounds.min.x;
float boundsMaxX = text.bounds.max.x;
for (int i = 0; i < characterCount; i++)
{
if (!textInfo.characterInfo[i].isVisible) continue;
int vertexIndex = textInfo.characterInfo[i].vertexIndex;
// Get the index of the mesh used by this character.
int materialIndex = textInfo.characterInfo[i].materialReferenceIndex;
vertices = textInfo.meshInfo[materialIndex].vertices;
// Compute the baseline mid point for each character
Vector3 offsetToMidBaseline = new Vector2(
(vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine);
// Apply offset to adjust our pivot point.
vertices[vertexIndex + 0] += -offsetToMidBaseline;
vertices[vertexIndex + 1] += -offsetToMidBaseline;
vertices[vertexIndex + 2] += -offsetToMidBaseline;
vertices[vertexIndex + 3] += -offsetToMidBaseline;
// Compute the angle of rotation for each character based on the animation curve
// Character's position relative to the bounds of the mesh.
float x0 = (offsetToMidBaseline.x - boundsMinX) / (boundsMaxX - boundsMinX);
float x1 = x0 + 0.0001f;
float y0 = vertexCurve.Evaluate(x0) * yCurveScaling;
float y1 = vertexCurve.Evaluate(x1) * yCurveScaling;
Vector3 horizontal = new Vector3(1, 0, 0);
Vector3 tangent = new Vector3(x1 * (boundsMaxX - boundsMinX) + boundsMinX, y1) -
new Vector3(offsetToMidBaseline.x, y0);
float dot = Mathf.Acos(Vector3.Dot(horizontal, tangent.normalized)) * Mathf.Rad2Deg;
Vector3 cross = Vector3.Cross(horizontal, tangent);
float angle = cross.z > 0 ? dot : 360 - dot;
matrix = Matrix4x4.TRS(new Vector3(0, y0, 0), Quaternion.Euler(0, 0, angle), Vector3.one);
vertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 0]);
vertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 1]);
vertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 2]);
vertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 3]);
vertices[vertexIndex + 0] += offsetToMidBaseline;
vertices[vertexIndex + 1] += offsetToMidBaseline;
vertices[vertexIndex + 2] += offsetToMidBaseline;
vertices[vertexIndex + 3] += offsetToMidBaseline;
// Upload the mesh with the revised information
text.UpdateVertexData();
}
isForceUpdatingMesh = false;
}
}