private IEnumerator DisplayCharacterByPage(Recitation recitation)
{
// Helper references
TMP_TextInfo textInfo = textScript.textInfo;
int characterCount = textInfo.characterCount;
// Display no characters to start
float appearTimeToWaitTimeRatio = 0.5f;
textScript.text = recitation.GetLocalizedText();
textScript.overflowMode = TextOverflowModes.Page;
textScript.alignment = TextAlignmentOptions.Midline;
// textScript.maxVisibleCharacters = 0;
textScript.ForceMeshUpdate();
// Store animation information
AnimationInfo animInfo = new AnimationInfo(
textScript.textInfo.characterCount, textScript, textInfo.CopyMeshInfoVertexData(), ClockIncrement);
// Apply Emotion
Coroutine[] emotionCoroutines = ApplyEmotion(animInfo);
// Display characters
float timePerPage = recitation.duration / textScript.textInfo.pageCount;
int charsPerPage = textScript.textInfo.characterCount / textScript.textInfo.pageCount;
int pagesWithExtra = textScript.textInfo.characterCount % textScript.textInfo.pageCount;
// Calculate time to appear
float appearTime = appearTimeToWaitTimeRatio * timePerPage;
// Show characters
int visibleCharacters = 0;
for (int i = 1; i <= textScript.textInfo.pageCount; i++)
{
// Set visible characters
textScript.pageToDisplay = i;
// Show text
int lastCharIndex = i == textScript.textInfo.pageCount ? textScript.textInfo.characterCount : textScript.textInfo.pageInfo[i - 1].lastCharacterIndex;
int charsSoFar = textScript.textInfo.pageInfo[i - 1].firstCharacterIndex;
int charsToAdd = lastCharIndex - charsSoFar;
for (float j = 0; j <= appearTime; j += ClockIncrement)
{
// Fix rounding errors
j = (float)System.Math.Round(j, ClockPrecisionDecimals);
// Set visible characters
float percentPageVisible = Mathf.Min(1f, j / appearTime);
int maxVisibleCharacters = charsSoFar + (int)Mathf.Ceil(charsToAdd * percentPageVisible);
// textScript.maxVisibleCharacters = maxVisibleCharacters;
AppearCharacters(ref visibleCharacters, maxVisibleCharacters, animInfo);
// Wait clock tick
yield return new WaitForSeconds(ClockIncrement);
}
// Wait a wait per page
yield return new WaitForSeconds(timePerPage - appearTime);
}
textScript.text = "";
// Stop with the emotions
foreach (Coroutine coroutine in emotionCoroutines)
{
StopCoroutine(coroutine);
}
}
private void AppearCharacters(ref int visibleCharacterIndex, int endIndex, AnimationInfo animInfo)
{
if (endIndex <= 0)
{
return;
}
TMP_TextInfo textInfo = textScript.textInfo;
for (; visibleCharacterIndex < endIndex; visibleCharacterIndex++)
{
// Appear the invisible characters
int vertexIndex = textInfo.characterInfo[visibleCharacterIndex].vertexIndex;
int materialIndex = textInfo.characterInfo[visibleCharacterIndex].materialReferenceIndex;
Color32[] newVertexColors = textInfo.meshInfo[materialIndex].colors32;
Color32 color = newVertexColors[vertexIndex + 0];
color.a = 255;
newVertexColors[vertexIndex + 0] = color;
newVertexColors[vertexIndex + 1] = color;
newVertexColors[vertexIndex + 2] = color;
newVertexColors[vertexIndex + 3] = color;
// Signal visible
animInfo.characterIsVisible[visibleCharacterIndex] = true;
}
textScript.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
}
private Coroutine[] ApplyEmotion(AnimationInfo animInfo)
{
TMP_TextInfo textInfo = textScript.textInfo;
int linkCount = textScript.textInfo.linkCount;
Coroutine[] coroutines = new Coroutine[linkCount];
for (int i = 0; i < linkCount; i++)
{
int start = textScript.textInfo.linkInfo[i].linkTextfirstCharacterIndex;
int end = start + textScript.textInfo.linkInfo[i].linkTextLength;
RecitationTextEmotion emotion = (RecitationTextEmotion)Enum.Parse(typeof(RecitationTextEmotion), textScript.textInfo.linkInfo[i].GetLinkID());
switch (emotion)
{
case RecitationTextEmotion.Jitter:
JitterAnimationConfig jitterConfig = new JitterAnimationConfig();
jitterConfig.curveScale = 1.0f;
jitterConfig.angleMultiplier = 1.0f;
jitterConfig.speedMultiplier = 0.85f;
jitterConfig.startIndex = start;
jitterConfig.endIndex = end;
jitterConfig.animationInfo = animInfo;
coroutines[i] = StartCoroutine(SpeechAnimationRoutines.AnimateJitter(jitterConfig));
break;
case RecitationTextEmotion.SingSong:
SingSongAnimationConfig singSongConfig = new SingSongAnimationConfig();
singSongConfig.angle = 10.0f;
singSongConfig.angleIncrement = 2.5f;
singSongConfig.speedMultiplier = 1.0f;
singSongConfig.startIndex = start;
singSongConfig.endIndex = end;
singSongConfig.animationInfo = animInfo;
// coroutines[i] = StartCoroutine(SpeechAnimationRoutines.AnimateSingSong(singSongConfig));
break;
default:
throw new Exception("Unknown text emotion: " + emotion);
}
}
return coroutines;
}
/// <summary>
/// Animate a jitter in the letters.
/// </summary>
/// <param name="jitterConfig">Configuration information for the jitter.</param>
/// <returns>IEnumerator for the coroutine.</returns>
public static IEnumerator AnimateJitter(JitterAnimationConfig jitterConfig)
{
Matrix4x4 matrix;
TMP_TextInfo textInfo = jitterConfig.animationInfo.textScript.textInfo;
AnimationInfo animInfo = jitterConfig.animationInfo;
float waitTime = animInfo.clockIncrement / jitterConfig.speedMultiplier;
Debug.Log(textInfo.characterCount);
while (true)
{
int i;
for (i = jitterConfig.startIndex; i < jitterConfig.endIndex; i++)
{
// Grab Character
TMP_CharacterInfo charInfo = textInfo.characterInfo[i];
if (charInfo.character == ' ')
{
continue;
}
if (!animInfo.characterIsVisible[i] || !charInfo.isVisible)
{
break;
}
int materialIndex = charInfo.materialReferenceIndex;
int vertexIndex = charInfo.vertexIndex;
// Get the cached vertices of the mesh used by this text element (character or sprite).
Vector3[] sourceVertices = animInfo.cachedMeshInfo[materialIndex].vertices;
// Determine the center point of each character.
Vector2 charMidBasline = (sourceVertices[vertexIndex + 0] + sourceVertices[vertexIndex + 2]) / 2;
// Need to translate all 4 vertices of each quad to aligned with middle of character / baseline.
// This is needed so the matrix TRS is applied at the origin for each character.
Vector3 offset = charMidBasline;
Vector3[] destinationVertices = textInfo.meshInfo[materialIndex].vertices;
destinationVertices[vertexIndex + 0] = sourceVertices[vertexIndex + 0] - offset;
destinationVertices[vertexIndex + 1] = sourceVertices[vertexIndex + 1] - offset;
destinationVertices[vertexIndex + 2] = sourceVertices[vertexIndex + 2] - offset;
destinationVertices[vertexIndex + 3] = sourceVertices[vertexIndex + 3] - offset;
// Apply Jitter
Vector3 jitterOffset = new Vector3(UnityEngine.Random.Range(-.25f, .25f), UnityEngine.Random.Range(-.25f, .25f), 0);
matrix = Matrix4x4.TRS(jitterOffset * jitterConfig.curveScale, Quaternion.Euler(0, 0, UnityEngine.Random.Range(-5f, 5f) * jitterConfig.angleMultiplier), Vector3.one);
destinationVertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 0]);
destinationVertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 1]);
destinationVertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 2]);
destinationVertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(destinationVertices[vertexIndex + 3]);
// Translate all 4 vertices of each quad back to their original positions
destinationVertices[vertexIndex + 0] += offset;
destinationVertices[vertexIndex + 1] += offset;
destinationVertices[vertexIndex + 2] += offset;
destinationVertices[vertexIndex + 3] += offset;
// Push changes into meshes
textInfo.meshInfo[materialIndex].mesh.vertices = textInfo.meshInfo[materialIndex].vertices;
animInfo.textScript.UpdateGeometry(textInfo.meshInfo[materialIndex].mesh, materialIndex);
}
yield return new WaitForSeconds(waitTime);
}
}
I had another post that was very similar. I spent all day trying to figure out the problem and came to the conclusion that vertex manipulation doesn’t seem to function properly with paging.
The above example starts with DisplayCharacterByPage. When that routine is started, it begins changing alpha from 0 to 255 for each character, page by page. It also kicks off another coroutine that should find all link tags and apply the jitter effect to the characters in the link tags.
What i’ve found with this code is that page 1 works fine. Anything beyond page 1 and the letters that are animating jitter will disappear.