using TMPro;
using UnityEngine;
///
/// Base class for drawing a Text Pro text following a particular curve
///
[ExecuteInEditMode]
public abstract class TMP_Curve : MonoBehaviour
{
///
/// The text component of interest
///
protected TMP_Text m_TextComponent;
///
/// True if the text must be updated at this frame
///
private bool m_forceUpdate;
public bool ForceUpdate
{
get { return m_forceUpdate; }
set { m_forceUpdate = value; }
}
private string cacheText = "";
///
/// Awake
///
private void Awake()
{
m_TextComponent = gameObject.GetComponent();
}
///
/// OnEnable
///
private void OnEnable()
{
//every time the object gets enabled, we have to force a re-creation of the text mesh
m_forceUpdate = true;
}
///
/// Update
///
protected void Update()
{
//if the text and the parameters are the same of the old frame, don't waste time in re-computing everything
if (cacheText == m_TextComponent.text && !ParametersHaveChanged() && !m_TextComponent.havePropertiesChanged && !m_forceUpdate)
{
return;
}
cacheText = m_TextComponent.text;
m_forceUpdate = false;
//during the loop, vertices represents the 4 vertices of a single character we're analyzing,
//while matrix is the roto-translation matrix that will rotate and scale the characters so that they will
//follow the curve
Vector3[] vertices;
Matrix4x4 matrix;
//Generate the mesh and get information about the text and the characters
m_TextComponent.ForceMeshUpdate();
TMP_TextInfo textInfo = m_TextComponent.textInfo;
int characterCount = textInfo.characterCount;
//if the string is empty, no need to waste time
if (characterCount == 0)
return;
//gets the bounds of the rectangle that contains the text
float boundsMinX = m_TextComponent.bounds.min.x;
float boundsMaxX = m_TextComponent.bounds.max.x;
//计算总长度
var width = m_TextComponent.preferredWidth;
//for each character
for (int i = 0; i < characterCount; i++)
{
//skip if it is invisible
if (!textInfo.characterInfo[i].isVisible)
continue;
//Get the index of the mesh used by this character, then the one of the material... and use all this data to get
//the 4 vertices of the rect that encloses this character. Store them in vertices
int vertexIndex = textInfo.characterInfo[i].vertexIndex;
int materialIndex = textInfo.characterInfo[i].materialReferenceIndex;
vertices = textInfo.meshInfo[materialIndex].vertices;
//Compute the baseline mid point for each character. This is the central point of the character.
//we will use this as the point representing this character for the geometry transformations
Vector3 charMidBaselinePos = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine);
//remove the central point from the vertices point. After this operation, every one of the four vertices
//will just have as coordinates the offset from the central position. This will come handy when will deal with the rotations
vertices[vertexIndex + 0] += -charMidBaselinePos;
vertices[vertexIndex + 1] += -charMidBaselinePos;
vertices[vertexIndex + 2] += -charMidBaselinePos;
vertices[vertexIndex + 3] += -charMidBaselinePos;
//compute the horizontal position of the character relative to the bounds of the box, in a range [0, 1]
//where 0 is the left border of the text and 1 is the right border
float zeroToOnePos = (charMidBaselinePos.x - boundsMinX) / (boundsMaxX - boundsMinX);
//get the transformation matrix, that maps the vertices, seen as offset from the central character point, to their final
//position that follows the curve
matrix = ComputeTransformationMatrix(charMidBaselinePos, zeroToOnePos, textInfo, i, width);
//apply the transformation, and obtain the final position and orientation of the 4 vertices representing this char
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] += charMidBaselinePos;
vertices[vertexIndex + 1] += charMidBaselinePos;
vertices[vertexIndex + 2] += charMidBaselinePos;
vertices[vertexIndex + 3] += charMidBaselinePos;
}
//Upload the mesh with the revised information
m_TextComponent.UpdateVertexData();
}
///
/// Method executed at every frame that checks if some parameters have been changed
///
///
protected abstract bool ParametersHaveChanged();
///
/// Computes the transformation matrix that maps the offsets of the vertices of each single character from
/// the character's center to the final destinations of the vertices so that the text follows a curve
///
/// Position of the central point of the character
/// Horizontal position of the character relative to the bounds of the box, in a range [0, 1]
/// Information on the text that we are showing
/// Index of the character we have to compute the transformation for
/// Transformation matrix to be applied to all vertices of the text
protected abstract Matrix4x4 ComputeTransformationMatrix(Vector3 charMidBaselinePos, float zeroToOnePos, TMP_TextInfo textInfo, int charIdx, float totalLength);
}