void OnRenderObject() { // Make sure I have the thigs I need to get the data to deform text if (m_TextComponent == null) { m_TextComponent = gameObject.GetComponent <TextMeshPro>(); } if (vertexCurve == null) { vertexCurve = gameObject.GetComponent <BezierSpline>(); } if (m_TextComponent) { Vector3[] vertexPositions; m_TextComponent.renderMode = TextRenderFlags.Render; m_TextComponent.ForceMeshUpdate(); m_TextComponent.renderMode = TextRenderFlags.DontRender; TMP_TextInfo textInfo = m_TextComponent.textInfo; int characterCount = textInfo.characterCount; if (characterCount >= 0) { vertexPositions = textInfo.meshInfo[0].vertices; float boundsMaxX = m_TextComponent.rectTransform.rect.width * 0.5f; float boundsMinX = -boundsMaxX; for (int i = 0; i < characterCount; i++) { if (!textInfo.characterInfo[i].isVisible) { continue; } int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Compute the baseline mid point for each character Vector3 offsetToMidBaseline = new Vector3((vertexPositions[vertexIndex + 0].x + vertexPositions[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine); Vector3 c0 = vertexPositions[vertexIndex + 0] - offsetToMidBaseline; Vector3 c1 = vertexPositions[vertexIndex + 1] - offsetToMidBaseline; Vector3 c2 = vertexPositions[vertexIndex + 2] - offsetToMidBaseline; Vector3 c3 = vertexPositions[vertexIndex + 3] - offsetToMidBaseline; float t = (offsetToMidBaseline.x - boundsMinX) / (boundsMaxX - boundsMinX); Vector3 point = transform.InverseTransformPoint(vertexCurve.GetPoint(t)) * curveScale; Vector3 xAxis = transform.InverseTransformDirection(vertexCurve.GetVelocity(t)).normalized; Vector3 yAxis = (Vector3.up - xAxis * xAxis.y).normalized; vertexPositions[vertexIndex + 0] = point + c0.x * xAxis + c0.y * yAxis; vertexPositions[vertexIndex + 1] = point + c1.x * xAxis + c1.y * yAxis; vertexPositions[vertexIndex + 2] = point + c2.x * xAxis + c2.y * yAxis; vertexPositions[vertexIndex + 3] = point + c3.x * xAxis + c3.y * yAxis; } // Upload the mesh with the revised information m_TextComponent.mesh.vertices = vertexPositions; m_TextComponent.mesh.uv = textInfo.meshInfo[0].uvs0; m_TextComponent.mesh.uv2 = textInfo.meshInfo[0].uvs2; m_TextComponent.mesh.colors32 = textInfo.meshInfo[0].colors32; m_TextComponent.mesh.RecalculateBounds(); // We need to update the bounds of the text object. } } }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> IEnumerator AnimateVertexColors() { // We force an update of the text object since it would only be updated at the end of the frame. Ie. before this code is executed on the first frame. // Alternatively, we could yield and wait until the end of the frame when the text object will be generated. m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = m_TextComponent.textInfo; Matrix4x4 matrix; var copyOfVertices = new Vector3[0][]; hasTextChanged = true; while (true) { // Allocate new vertices if (hasTextChanged) { if (copyOfVertices.Length < textInfo.meshInfo.Length) { copyOfVertices = new Vector3[textInfo.meshInfo.Length][]; } for (var i = 0; i < textInfo.meshInfo.Length; i++) { var length = textInfo.meshInfo[i].vertices.Length; copyOfVertices[i] = new Vector3[length]; } hasTextChanged = false; } var characterCount = textInfo.characterCount; // If No Characters then just yield and wait for some text to be added if (characterCount == 0) { yield return(new WaitForSeconds(0.25f)); continue; } var lineCount = textInfo.lineCount; // Iterate through each line of the text. for (var i = 0; i < lineCount; i++) { var first = textInfo.lineInfo[i].firstCharacterIndex; var last = textInfo.lineInfo[i].lastCharacterIndex; // Determine the center of each line Vector3 centerOfLine = (textInfo.characterInfo[first].bottomLeft + textInfo.characterInfo[last].topRight) / 2; Quaternion rotation = Quaternion.Euler(0, 0, Random.Range(-0.25f, 0.25f) * RotationMultiplier); // Iterate through each character of the line. for (var j = first; j <= last; j++) { // Skip characters that are not visible and thus have no geometry to manipulate. if (!textInfo.characterInfo[j].isVisible) { continue; } // Get the index of the material used by the current character. var materialIndex = textInfo.characterInfo[j].materialReferenceIndex; // Get the index of the first vertex used by this text element. var vertexIndex = textInfo.characterInfo[j].vertexIndex; // Get the vertices of the mesh used by this text element (character or sprite). var sourceVertices = textInfo.meshInfo[materialIndex].vertices; // Need to translate all 4 vertices of each quad to aligned with center of character. // This is needed so the matrix TRS is applied at the origin for each character. copyOfVertices[materialIndex][vertexIndex + 0] = sourceVertices[vertexIndex + 0] - centerOfLine; copyOfVertices[materialIndex][vertexIndex + 1] = sourceVertices[vertexIndex + 1] - centerOfLine; copyOfVertices[materialIndex][vertexIndex + 2] = sourceVertices[vertexIndex + 2] - centerOfLine; copyOfVertices[materialIndex][vertexIndex + 3] = sourceVertices[vertexIndex + 3] - centerOfLine; // Determine the random scale change for each character. var randomScale = Random.Range(0.995f - 0.001f * ScaleMultiplier, 1.005f + 0.001f * ScaleMultiplier); // Setup the matrix rotation. matrix = Matrix4x4.TRS(Vector3.one, rotation, Vector3.one * randomScale); // Apply the matrix TRS to the individual characters relative to the center of the current line. copyOfVertices[materialIndex][vertexIndex + 0] = matrix.MultiplyPoint3x4(copyOfVertices[materialIndex][vertexIndex + 0]); copyOfVertices[materialIndex][vertexIndex + 1] = matrix.MultiplyPoint3x4(copyOfVertices[materialIndex][vertexIndex + 1]); copyOfVertices[materialIndex][vertexIndex + 2] = matrix.MultiplyPoint3x4(copyOfVertices[materialIndex][vertexIndex + 2]); copyOfVertices[materialIndex][vertexIndex + 3] = matrix.MultiplyPoint3x4(copyOfVertices[materialIndex][vertexIndex + 3]); // Revert the translation change. copyOfVertices[materialIndex][vertexIndex + 0] += centerOfLine; copyOfVertices[materialIndex][vertexIndex + 1] += centerOfLine; copyOfVertices[materialIndex][vertexIndex + 2] += centerOfLine; copyOfVertices[materialIndex][vertexIndex + 3] += centerOfLine; } } // Push changes into meshes for (var i = 0; i < textInfo.meshInfo.Length; i++) { textInfo.meshInfo[i].mesh.vertices = copyOfVertices[i]; m_TextComponent.UpdateGeometry(textInfo.meshInfo[i].mesh, i); } yield return(new WaitForSeconds(0.1f)); } }
public void StartEffect(TMP_TextInfo textInfo, Color[] colors, Vector3[] vertices, ReadOnlyCollection <Vector3> originVertices) { // Do nothing. }
/// <summary> /// Method to draw a rectangle around each character. /// </summary> /// <param name="text"></param> void DrawCharactersBounds() { TMP_TextInfo textInfo = m_TextComponent.textInfo; for (int i = 0; i < textInfo.characterCount; i++) { // Draw visible as well as invisible characters TMP_CharacterInfo cInfo = textInfo.characterInfo[i]; bool isCharacterVisible = i >= m_TextComponent.maxVisibleCharacters || cInfo.lineNumber >= m_TextComponent.maxVisibleLines || (m_TextComponent.overflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; if (!isCharacterVisible) { continue; } // Get Bottom Left and Top Right position of the current character Vector3 bottomLeft = m_Transform.TransformPoint(cInfo.bottomLeft); Vector3 topLeft = m_Transform.TransformPoint(new Vector3(cInfo.topLeft.x, cInfo.topLeft.y, 0)); Vector3 topRight = m_Transform.TransformPoint(cInfo.topRight); Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(cInfo.bottomRight.x, cInfo.bottomRight.y, 0)); Color color = cInfo.isVisible ? Color.yellow : Color.grey; DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, color); // Baseline Vector3 baselineStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0); Vector3 baselineEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(baselineStart, baselineEnd); // Draw Ascender & Descender for each character. Vector3 ascenderStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0); Vector3 ascenderEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0); Vector3 descenderStart = new Vector3(bottomLeft.x, m_Transform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0); Vector3 descenderEnd = new Vector3(bottomRight.x, m_Transform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(ascenderStart, ascenderEnd); Gizmos.DrawLine(descenderStart, descenderEnd); // Draw Cap Height float capHeight = cInfo.baseLine + cInfo.fontAsset.fontInfo.CapHeight * cInfo.scale; Vector3 capHeightStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0); Vector3 capHeightEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(capHeightStart, capHeightEnd); // Draw xAdvance for each character. float xAdvance = m_Transform.TransformPoint(cInfo.xAdvance, 0, 0).x; Vector3 topAdvance = new Vector3(xAdvance, topLeft.y, 0); Vector3 bottomAdvance = new Vector3(xAdvance, bottomLeft.y, 0); Gizmos.color = Color.green; Gizmos.DrawLine(topAdvance, bottomAdvance); } }
void OnDrawGizmos() { // Update Text Statistics TMP_TextInfo textInfo = m_TextComponent.textInfo; ObjectStats = "Characters: " + textInfo.characterCount + " Words: " + textInfo.wordCount + " Spaces: " + textInfo.spaceCount + " Sprites: " + textInfo.spriteCount + " Links: " + textInfo.linkCount + "\nLines: " + textInfo.lineCount + " Pages: " + textInfo.pageCount; // Draw Quads around each of the Characters #region Draw Characters if (ShowCharacters) { DrawCharactersBounds(); } #endregion // Draw Quads around each of the words #region Draw Words if (ShowWords) { DrawWordBounds(); } #endregion // Draw Quads around each of the words #region Draw Links if (ShowLinks) { DrawLinkBounds(); } #endregion // Draw Quads around each line #region Draw Lines if (ShowLines) { DrawLineBounds(); } #endregion // Draw Quad around the bounds of the text #region Draw Bounds if (ShowMeshBounds) { DrawBounds(); } #endregion // Draw Quad around the rendered region of the text. #region Draw Text Bounds if (ShowTextBounds) { DrawTextBounds(); } #endregion }
// Token: 0x060045AF RID: 17839 RVA: 0x00177B74 File Offset: 0x00175F74 private IEnumerator AnimateVertexColors() { this.m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = this.m_TextComponent.textInfo; int loopCount = 0; this.hasTextChanged = true; VertexJitter.VertexAnim[] vertexAnim = new VertexJitter.VertexAnim[1024]; for (int i = 0; i < 1024; i++) { vertexAnim[i].angleRange = UnityEngine.Random.Range(10f, 25f); vertexAnim[i].speed = UnityEngine.Random.Range(1f, 3f); } TMP_MeshInfo[] cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); for (;;) { if (this.hasTextChanged) { cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); this.hasTextChanged = false; } int characterCount = textInfo.characterCount; if (characterCount == 0) { yield return(new WaitForSeconds(0.25f)); } else { for (int j = 0; j < characterCount; j++) { TMP_CharacterInfo tmp_CharacterInfo = textInfo.characterInfo[j]; if (tmp_CharacterInfo.isVisible) { VertexJitter.VertexAnim vertexAnim2 = vertexAnim[j]; int materialReferenceIndex = textInfo.characterInfo[j].materialReferenceIndex; int vertexIndex = textInfo.characterInfo[j].vertexIndex; Vector3[] vertices = cachedMeshInfo[materialReferenceIndex].vertices; Vector2 v = (vertices[vertexIndex] + vertices[vertexIndex + 2]) / 2f; Vector3 b = v; Vector3[] vertices2 = textInfo.meshInfo[materialReferenceIndex].vertices; vertices2[vertexIndex] = vertices[vertexIndex] - b; vertices2[vertexIndex + 1] = vertices[vertexIndex + 1] - b; vertices2[vertexIndex + 2] = vertices[vertexIndex + 2] - b; vertices2[vertexIndex + 3] = vertices[vertexIndex + 3] - b; vertexAnim2.angle = Mathf.SmoothStep(-vertexAnim2.angleRange, vertexAnim2.angleRange, Mathf.PingPong((float)loopCount / 25f * vertexAnim2.speed, 1f)); Vector3 a = new Vector3(UnityEngine.Random.Range(-0.25f, 0.25f), UnityEngine.Random.Range(-0.25f, 0.25f), 0f); Matrix4x4 matrix = Matrix4x4.TRS(a * this.CurveScale, Quaternion.Euler(0f, 0f, UnityEngine.Random.Range(-5f, 5f) * this.AngleMultiplier), Vector3.one); vertices2[vertexIndex] = matrix.MultiplyPoint3x4(vertices2[vertexIndex]); vertices2[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices2[vertexIndex + 1]); vertices2[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices2[vertexIndex + 2]); vertices2[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices2[vertexIndex + 3]); vertices2[vertexIndex] += b; vertices2[vertexIndex + 1] += b; vertices2[vertexIndex + 2] += b; vertices2[vertexIndex + 3] += b; vertexAnim[j] = vertexAnim2; } } for (int k = 0; k < textInfo.meshInfo.Length; k++) { textInfo.meshInfo[k].mesh.vertices = textInfo.meshInfo[k].vertices; this.m_TextComponent.UpdateGeometry(textInfo.meshInfo[k].mesh, k); } loopCount++; yield return(new WaitForSeconds(0.1f)); } } yield break; }
public void AnimateText() { //Y'know. m_TextComponent.ForceMeshUpdate(); //we gotta get the mesh info. we gotta. TMP_TextInfo textInfo = m_TextComponent.textInfo; TMP_MeshInfo[] cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); hasTextChanged = true; //materials. int numMaterials; Vector3[][] sourceVertices = new Vector3[1][]; Vector3[][] destVertices = new Vector3[1][]; Color[][] destColors = new Color[1][]; //infinite loop //while (true) { // we gotta get that NEW mesh if the text is diffrntetnt if (hasTextChanged) { // Update the copy of the vertex data for the text object. cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); numMaterials = textInfo.materialCount; sourceVertices = new Vector3[numMaterials][]; destVertices = new Vector3[numMaterials][]; destColors = new Color[numMaterials][]; //Y'see, like //We can have more than 1 material for a text thing, so we gotta have multiple arrays cuz they can't be shared //among different materials. //I don't know why I'm commenting the stuff I didn't write. for (int i = 0; i < numMaterials; i++) { int numVerts = cachedMeshInfo[i].vertices.Length; sourceVertices[i] = new Vector3[numVerts]; destVertices[i] = new Vector3[numVerts]; destColors[i] = new Color[numVerts]; System.Array.Copy(cachedMeshInfo[i].vertices, sourceVertices[i], numVerts); } hasTextChanged = false; } int characterCount = textInfo.characterCount; if (characterCount == 0) { //oh no, we don't have any text ;( //so sad. //better luck next time. //yield return new WaitForSeconds(0.25f); //continue; } int curSegment = 0; //We have passed the test above and have characters. Praise Talos. for (int i = 0; i < characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; //why was there a comment on this line. //like //it's pretty friggin obvious what's going on here. if (!charInfo.isVisible) { continue; } // Retrieve the pre-computed animation data for the given character. //VertexAnim vertAnim = vertexAnim[i]; //JESSE: this does not exist, does not know what vertexAnim[i] is // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[i].vertexIndex; //I don't fuggin know how AnimType t = AnimType.Nope; //keep track of which animated segment we're in //...If there are any, anyway //print("hello?"); if (curSegment < segments.Count) { if (i >= segments[curSegment].stert && i < segments[curSegment].ernd) { t = segments[curSegment].type; //print("we displayin a segment up in this bitch"); } else if (i > segments[curSegment].ernd) { curSegment++; } } //we did it destColors[materialIndex][vertexIndex + 0] = textmesh.color; destColors[materialIndex][vertexIndex + 1] = textmesh.color; destColors[materialIndex][vertexIndex + 2] = textmesh.color; destColors[materialIndex][vertexIndex + 3] = textmesh.color; //do the thing switch (t) { case AnimType.Jitter: Jitter(destVertices[materialIndex], sourceVertices[materialIndex], vertexIndex); break; case AnimType.Pulse: { float intensity = Pulse(destVertices[materialIndex], sourceVertices[materialIndex], vertexIndex); Color textColor = Color.Lerp(textmesh.color, PulseColor, intensity); //textColor = Color.Lerp(); destColors[materialIndex][vertexIndex] = textColor; destColors[materialIndex][vertexIndex + 1] = textColor; destColors[materialIndex][vertexIndex + 2] = textColor; destColors[materialIndex][vertexIndex + 3] = textColor; } break; case AnimType.Flow: Flow(ref destVertices[materialIndex], ref sourceVertices[materialIndex], vertexIndex); break; default: Nothing(destVertices[materialIndex], sourceVertices[materialIndex], vertexIndex); break; } System.Array.Copy(destVertices[materialIndex], textInfo.meshInfo[materialIndex].vertices, destVertices[materialIndex].Length); //System.Array.Copy(destColors[materialIndex], textInfo.meshInfo[materialIndex].vertices, destVertices[materialIndex].Length); // vertexAnim[i] = vertAnim; //error, vertexanim[i] does not exist } for (int i = 0; i < textInfo.meshInfo.Length; i++) { textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices; if (i < destColors.Length) { textInfo.meshInfo[i].mesh.colors = destColors[i]; } m_TextComponent.UpdateGeometry(textInfo.meshInfo[i].mesh, i); } //yield return new WaitForSeconds(updateDT); } }
public override void ModifyCharacter(CharacterData characterData, TMP_Text textComponent, TMP_TextInfo textInfo, float progress, TMP_MeshInfo[] meshInfo) { var materialIndex = characterData.MaterialIndex; var vertexIndex = characterData.VertexIndex; var sourceVertices = meshInfo[materialIndex].vertices; Vector2 charMidBasline = (sourceVertices[vertexIndex + 0] + sourceVertices[vertexIndex + 2]) / 2; Vector3 offset = charMidBasline; var 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; var finalScale = Vector3.one; if (animateScale) { if (applyOnX) { finalScale.x = scaleCurve.Evaluate(characterData.Progress); } if (applyOnY) { finalScale.y = scaleCurve.Evaluate(characterData.Progress); } if (applyOnZ) { finalScale.z = scaleCurve.Evaluate(characterData.Progress); } } var finalPosition = Vector3.zero; if (animatePosition) { finalPosition = positionMultiplier * positionCurve.Evaluate(characterData.Progress); } var finalQuaternion = Quaternion.identity; if (animateRotation) { finalQuaternion = Quaternion.Euler(rotationMultiplier * rotationCurve.Evaluate(characterData.Progress)); } var matrix = Matrix4x4.TRS(finalPosition, finalQuaternion, finalScale); 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]); destinationVertices[vertexIndex + 0] += offset; destinationVertices[vertexIndex + 1] += offset; destinationVertices[vertexIndex + 2] += offset; destinationVertices[vertexIndex + 3] += offset; }
/// <summary> /// Method to draw a rectangle around each character. /// </summary> /// <param name="text"></param> void DrawCharactersBounds() { TMP_TextInfo textInfo = mTextComponent.textInfo; for (int i = 0; i < textInfo.characterCount; i++) { // Draw visible as well as invisible characters TMP_CharacterInfo cInfo = textInfo.characterInfo[i]; bool isCharacterVisible = i >= mTextComponent.maxVisibleCharacters || cInfo.lineNumber >= mTextComponent.maxVisibleLines || (mTextComponent.overflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != mTextComponent.pageToDisplay) ? false : true; if (!isCharacterVisible) { continue; } // Get Bottom Left and Top Right position of the current character Vector3 bottomLeft = mTransform.TransformPoint(cInfo.bottomLeft); Vector3 topLeft = mTransform.TransformPoint(new Vector3(cInfo.topLeft.x, cInfo.topLeft.y, 0)); Vector3 topRight = mTransform.TransformPoint(cInfo.topRight); Vector3 bottomRight = mTransform.TransformPoint(new Vector3(cInfo.bottomRight.x, cInfo.bottomRight.y, 0)); Color color = cInfo.isVisible ? Color.yellow : Color.grey; DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, color); // Baseline Vector3 baselineStart = new Vector3(topLeft.x, mTransform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0); Vector3 baselineEnd = new Vector3(topRight.x, mTransform.TransformPoint(new Vector3(0, cInfo.baseLine, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(baselineStart, baselineEnd); // Draw Ascender & Descender for each character. Vector3 ascenderStart = new Vector3(topLeft.x, mTransform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0); Vector3 ascenderEnd = new Vector3(topRight.x, mTransform.TransformPoint(new Vector3(0, cInfo.ascender, 0)).y, 0); Vector3 descenderStart = new Vector3(bottomLeft.x, mTransform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0); Vector3 descenderEnd = new Vector3(bottomRight.x, mTransform.TransformPoint(new Vector3(0, cInfo.descender, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(ascenderStart, ascenderEnd); Gizmos.DrawLine(descenderStart, descenderEnd); // Draw Cap Height float capHeight = cInfo.baseLine + cInfo.fontAsset.faceInfo.capLine * cInfo.scale; Vector3 capHeightStart = new Vector3(topLeft.x, mTransform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0); Vector3 capHeightEnd = new Vector3(topRight.x, mTransform.TransformPoint(new Vector3(0, capHeight, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(capHeightStart, capHeightEnd); // Draw Centerline float meanline = cInfo.baseLine + cInfo.fontAsset.faceInfo.meanLine * cInfo.scale; Vector3 centerlineStart = new Vector3(topLeft.x, mTransform.TransformPoint(new Vector3(0, meanline, 0)).y, 0); Vector3 centerlineEnd = new Vector3(topRight.x, mTransform.TransformPoint(new Vector3(0, meanline, 0)).y, 0); Gizmos.color = Color.cyan; Gizmos.DrawLine(centerlineStart, centerlineEnd); // Draw Origin for each character. float gizmoSize = (ascenderEnd.y - descenderEnd.y) * 0.02f; Vector3 origin = mTransform.TransformPoint(cInfo.origin, cInfo.baseLine, 0); Vector3 originBl = new Vector3(origin.x - gizmoSize, origin.y - gizmoSize, 0); Vector3 originTl = new Vector3(originBl.x, origin.y + gizmoSize, 0); Vector3 originTr = new Vector3(origin.x + gizmoSize, originTl.y, 0); Vector3 originBr = new Vector3(originTr.x, originBl.y, 0); Gizmos.color = new Color(1, 0.5f, 0); Gizmos.DrawLine(originBl, originTl); Gizmos.DrawLine(originTl, originTr); Gizmos.DrawLine(originTr, originBr); Gizmos.DrawLine(originBr, originBl); // Draw xAdvance for each character. gizmoSize = (ascenderEnd.y - descenderEnd.y) * 0.04f; float xAdvance = mTransform.TransformPoint(cInfo.xAdvance, 0, 0).x; Vector3 topAdvance = new Vector3(xAdvance, baselineStart.y + gizmoSize, 0); Vector3 bottomAdvance = new Vector3(xAdvance, baselineStart.y - gizmoSize, 0); Vector3 leftAdvance = new Vector3(xAdvance - gizmoSize, baselineStart.y, 0); Vector3 rightAdvance = new Vector3(xAdvance + gizmoSize, baselineStart.y, 0); Gizmos.color = Color.green; Gizmos.DrawLine(topAdvance, bottomAdvance); Gizmos.DrawLine(leftAdvance, rightAdvance); } }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> public IEnumerator AnimateVertexColors() { int visibleCount = 0; TMP_TextInfo textInfo = m_TextComponent.textInfo; Color32[] newVertexColors; Color32 c0 = m_TextComponent.color; // We wait one fram to avoid a collision with other effect leading to a miss on first character color animation yield return(null); while (visibleCount < textInfo.characterCount) { // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[visibleCount].materialReferenceIndex; // Get the vertex colors of the mesh used by this text element (character or sprite). newVertexColors = textInfo.meshInfo[materialIndex].colors32; // Update actual letter // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[visibleCount].vertexIndex; c0 = new Color32(255, 255, 255, 125); newVertexColors[vertexIndex + 0] = c0; newVertexColors[vertexIndex + 1] = c0; newVertexColors[vertexIndex + 2] = c0; newVertexColors[vertexIndex + 3] = c0; if (visibleCount > 1) { vertexIndex = textInfo.characterInfo[visibleCount - 1].vertexIndex; c0 = new Color32(255, 255, 255, 195); newVertexColors[vertexIndex + 0] = c0; newVertexColors[vertexIndex + 1] = c0; newVertexColors[vertexIndex + 2] = c0; newVertexColors[vertexIndex + 3] = c0; vertexIndex = textInfo.characterInfo[visibleCount - 2].vertexIndex; c0 = new Color32(255, 255, 255, 255); newVertexColors[vertexIndex + 0] = c0; newVertexColors[vertexIndex + 1] = c0; newVertexColors[vertexIndex + 2] = c0; newVertexColors[vertexIndex + 3] = c0; } m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); yield return(null); yield return(null); yield return(null); visibleCount++; } int count = 0; while (count <= 1) { // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[visibleCount - 1].materialReferenceIndex; // Get the vertex colors of the mesh used by this text element (character or sprite). newVertexColors = textInfo.meshInfo[materialIndex].colors32; // Update actual letter // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[visibleCount - 1].vertexIndex; switch (count) { case 0: c0 = new Color32(255, 255, 255, 195); break; case 1: c0 = new Color32(255, 255, 255, 255); break; } newVertexColors[vertexIndex + 0] = c0; newVertexColors[vertexIndex + 1] = c0; newVertexColors[vertexIndex + 2] = c0; newVertexColors[vertexIndex + 3] = c0; m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); yield return(null); yield return(null); yield return(null); count++; } }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> IEnumerator AnimateVertexColors() { // We force an update of the text object since it would only be updated at the end of the frame. Ie. before this code is executed on the first frame. // Alternatively, we could yield and wait until the end of the frame when the text object will be generated. m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = m_TextComponent.textInfo; Matrix4x4 matrix; TMP_MeshInfo[] cachedMeshInfoVertexData = textInfo.CopyMeshInfoVertexData(); // Allocations for sorting of the modified scales List <float> modifiedCharScale = new List <float>(); List <int> scaleSortingOrder = new List <int>(); hasTextChanged = true; while (true) { // Allocate new vertices if (hasTextChanged) { // Get updated vertex data cachedMeshInfoVertexData = textInfo.CopyMeshInfoVertexData(); hasTextChanged = false; } int characterCount = textInfo.characterCount; // If No Characters then just yield and wait for some text to be added if (characterCount == 0) { yield return(new WaitForSeconds(0.25f)); continue; } // Clear list of character scales modifiedCharScale.Clear(); scaleSortingOrder.Clear(); for (int i = 0; i < characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; // Skip characters that are not visible and thus have no geometry to manipulate. if (!charInfo.isVisible) { continue; } // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Get the cached vertices of the mesh used by this text element (character or sprite). Vector3[] sourceVertices = cachedMeshInfoVertexData[materialIndex].vertices; // Determine the center point of each character at the baseline. //Vector2 charMidBasline = new Vector2((sourceVertices[vertexIndex + 0].x + sourceVertices[vertexIndex + 2].x) / 2, charInfo.baseLine); // 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; //Vector3 jitterOffset = new Vector3(Random.Range(-.25f, .25f), Random.Range(-.25f, .25f), 0); // Determine the random scale change for each character. float randomScale = Random.Range(1f, 1.5f); // Add modified scale and index modifiedCharScale.Add(randomScale); scaleSortingOrder.Add(modifiedCharScale.Count - 1); // Setup the matrix for the scale change. //matrix = Matrix4x4.TRS(jitterOffset, Quaternion.Euler(0, 0, Random.Range(-5f, 5f)), Vector3.one * randomScale); matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, Vector3.one * randomScale); 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]); destinationVertices[vertexIndex + 0] += offset; destinationVertices[vertexIndex + 1] += offset; destinationVertices[vertexIndex + 2] += offset; destinationVertices[vertexIndex + 3] += offset; // Restore Source UVS which have been modified by the sorting Vector2[] sourceUVs0 = cachedMeshInfoVertexData[materialIndex].uvs0; Vector2[] destinationUVs0 = textInfo.meshInfo[materialIndex].uvs0; destinationUVs0[vertexIndex + 0] = sourceUVs0[vertexIndex + 0]; destinationUVs0[vertexIndex + 1] = sourceUVs0[vertexIndex + 1]; destinationUVs0[vertexIndex + 2] = sourceUVs0[vertexIndex + 2]; destinationUVs0[vertexIndex + 3] = sourceUVs0[vertexIndex + 3]; // Restore Source Vertex Colors Color32[] sourceColors32 = cachedMeshInfoVertexData[materialIndex].colors32; Color32[] destinationColors32 = textInfo.meshInfo[materialIndex].colors32; destinationColors32[vertexIndex + 0] = sourceColors32[vertexIndex + 0]; destinationColors32[vertexIndex + 1] = sourceColors32[vertexIndex + 1]; destinationColors32[vertexIndex + 2] = sourceColors32[vertexIndex + 2]; destinationColors32[vertexIndex + 3] = sourceColors32[vertexIndex + 3]; } // Push changes into meshes for (int i = 0; i < textInfo.meshInfo.Length; i++) { //// Sort Quads based modified scale scaleSortingOrder.Sort((a, b) => modifiedCharScale[a].CompareTo(modifiedCharScale[b])); textInfo.meshInfo[i].SortGeometry(scaleSortingOrder); // Updated modified vertex attributes textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices; textInfo.meshInfo[i].mesh.uv = textInfo.meshInfo[i].uvs0; textInfo.meshInfo[i].mesh.colors32 = textInfo.meshInfo[i].colors32; m_TextComponent.UpdateGeometry(textInfo.meshInfo[i].mesh, i); } yield return(new WaitForSeconds(0.1f)); } }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> public IEnumerator AnimateVertex() { // We force an update of the text object since it would only be updated at the end of the frame. Ie. before this code is executed on the first frame. // Alternatively, we could yield and wait until the end of the frame when the text object will be generated. m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = m_TextComponent.textInfo; Matrix4x4 matrix; int loopCount = 0; // Create an Array which contains pre-computed Angle Ranges and Speeds for a bunch of characters. VertexAnim[] vertexAnim = new VertexAnim[1024]; for (int i = 0; i < 1024; i++) { vertexAnim[i].speed = Random.Range(1f, 3f); } // Cache the vertex data of the text object as the Jitter FX is applied to the original position of the characters. TMP_MeshInfo[] cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); while (true) { // Get new copy of vertex data if the text has changed. if (_hasTextChanged) { // Update the copy of the vertex data for the text object. cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); _hasTextChanged = false; } int characterCount = textInfo.characterCount; // If No Characters then just yield and wait for some text to be added while (characterCount == 0) { characterCount = textInfo.characterCount; yield return(null); } for (int i = 0; i < characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; // Skip characters that are not visible and thus have no geometry to manipulate. if (!charInfo.isVisible) { continue; } // Retrieve the pre-computed animation data for the given character. VertexAnim vertAnim = vertexAnim[i]; // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Get the cached vertices of the mesh used by this text element (character or sprite). Vector3[] sourceVertices = 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; Vector3 jitterOffset = new Vector3(0, Mathf.Sin(Time.time * 5f + i), 0); matrix = Matrix4x4.TRS(jitterOffset * CurveScale, Quaternion.Euler(0, 0, 0), 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]); destinationVertices[vertexIndex + 0] += offset; destinationVertices[vertexIndex + 1] += offset; destinationVertices[vertexIndex + 2] += offset; destinationVertices[vertexIndex + 3] += offset; vertexAnim[i] = vertAnim; } // Push changes into meshes for (int i = 0; i < textInfo.meshInfo.Length; i++) { textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices; m_TextComponent.UpdateGeometry(textInfo.meshInfo[i].mesh, i); } loopCount += 1; yield return(new WaitForSeconds(0.1f)); } }
/// <summary> /// Update /// </summary> 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 (!m_forceUpdate && !m_TextComponent.havePropertiesChanged && !ParametersHaveChanged()) { return; } 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; //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); //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]); } //Upload the mesh with the revised information m_TextComponent.UpdateVertexData(); }
/// <summary> /// 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 /// </summary> /// <param name="charMidBaselinePos">Position of the central point of the character</param> /// <param name="zeroToOnePos">Horizontal position of the character relative to the bounds of the box, in a range [0, 1]</param> /// <param name="textInfo">Information on the text that we are showing</param> /// <param name="charIdx">Index of the character we have to compute the transformation for</param> /// <returns>Transformation matrix to be applied to all vertices of the text</returns> protected abstract Matrix4x4 ComputeTransformationMatrix(Vector3 charMidBaselinePos, float zeroToOnePos, TMP_TextInfo textInfo, int charIdx);
void OnDrawGizmos() { // Update Text Statistics switch (m_textObjectType) { case objectType.TextMeshPro: TMP_TextInfo textInfo = m_TextMeshPro.textInfo; ObjectStats = "Characters: " + textInfo.characterCount + " Words: " + textInfo.wordCount + " Spaces: " + textInfo.spaceCount + " Sprites: " + textInfo.spriteCount + " Links: " + textInfo.linkCount + "\nLines: " + textInfo.lineCount + " Pages: " + textInfo.pageCount; break; case objectType.TextMeshProUI: textInfo = m_TextMeshProUI.textInfo; ObjectStats = "Characters: " + textInfo.characterCount + " Words: " + textInfo.wordCount + " Spaces: " + textInfo.spaceCount + " Sprites: " + textInfo.spriteCount + " Links: " + textInfo.linkCount + "\nLines: " + textInfo.lineCount + " Pages: " + textInfo.pageCount; break; } // Draw Quads around each of the Characters #region Draw Characters if (ShowCharacters) { switch (m_textObjectType) { case objectType.TextMeshPro: DrawCharacters(m_TextMeshPro); break; case objectType.TextMeshProUI: DrawCharacters(m_TextMeshProUI); break; } } #endregion // Draw Quads around each of the words #region Draw Words if (ShowWords) { switch (m_textObjectType) { case objectType.TextMeshPro: DrawWords(m_TextMeshPro); break; case objectType.TextMeshProUI: DrawWords(m_TextMeshProUI); break; } } #endregion // Draw Quads around each of the words #region Draw Links if (ShowLinks) { switch (m_textObjectType) { case objectType.TextMeshPro: DrawLinks(m_TextMeshPro); break; case objectType.TextMeshProUI: DrawLinks(m_TextMeshProUI); break; } } #endregion // Draw Quads around each line #region Draw Lines if (ShowLines) { switch (m_textObjectType) { case objectType.TextMeshPro: DrawLines(m_TextMeshPro); break; case objectType.TextMeshProUI: DrawLines(m_TextMeshProUI); break; } } #endregion // Draw Quads around the bounds of the text #region Draw Bounds if (ShowBounds) { switch (m_textObjectType) { case objectType.TextMeshPro: DrawBounds(m_TextMeshPro); break; case objectType.TextMeshProUI: DrawBounds(m_TextMeshProUI); break; } } #endregion }
void OnDrawGizmos() { if (mTextComponent == null) { mTextComponent = gameObject.GetComponent <TMP_Text>(); if (mTextComponent == null) { return; } if (mTransform == null) { mTransform = gameObject.GetComponent <Transform>(); } } // Update Text Statistics TMP_TextInfo textInfo = mTextComponent.textInfo; objectStats = "Characters: " + textInfo.characterCount + " Words: " + textInfo.wordCount + " Spaces: " + textInfo.spaceCount + " Sprites: " + textInfo.spriteCount + " Links: " + textInfo.linkCount + "\nLines: " + textInfo.lineCount + " Pages: " + textInfo.pageCount; // Draw Quads around each of the Characters #region Draw Characters if (showCharacters) { DrawCharactersBounds(); } #endregion // Draw Quads around each of the words #region Draw Words if (showWords) { DrawWordBounds(); } #endregion // Draw Quads around each of the words #region Draw Links if (showLinks) { DrawLinkBounds(); } #endregion // Draw Quads around each line #region Draw Lines if (showLines) { DrawLineBounds(); } #endregion // Draw Quad around the bounds of the text #region Draw Bounds if (showMeshBounds) { DrawBounds(); } #endregion // Draw Quad around the rendered region of the text. #region Draw Text Bounds if (showTextBounds) { DrawTextBounds(); } #endregion }
/// <summary> /// 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 /// </summary> /// <param name="charMidBaselinePosfloat">Position of the central point of the character</param> /// <param name="zeroToOnePos">Horizontal position of the character relative to the bounds of the box, in a range [0, 1]</param> /// <param name="textInfo">Information on the text that we are showing</param> /// <param name="charIdx">Index of the character we have to compute the transformation for</param> /// <returns>Transformation matrix to be applied to all vertices of the text</returns> protected override Matrix4x4 ComputeTransformationMatrix(Vector3 charMidBaselinePos, float zeroToOnePos, TMP_TextInfo textInfo, int charIdx) { //compute the angle at which to show this character. //We want the string to be centered at the top point of the circle, so we first convert the position from a range [0, 1] //to a [-0.5, 0.5] one and then add m_angularOffset degrees, to make it centered on the desired point float angle = ((zeroToOnePos - 0.5f) * m_arcDegrees + m_angularOffset) * Mathf.Deg2Rad; //we need radians for sin and cos //compute the coordinates of the new position of the central point of the character. Use sin and cos since we are on a circle. //Notice that we have to do some extra calculations because we have to take in count that text may be on multiple lines float x0 = Mathf.Cos(angle); float y0 = Mathf.Sin(angle); float radiusForThisLine = m_radius - textInfo.lineInfo[0].lineExtents.max.y * textInfo.characterInfo[charIdx].lineNumber; Vector2 newMideBaselinePos = new Vector2(x0 * radiusForThisLine, -y0 * radiusForThisLine); //actual new position of the character //compute the trasformation matrix: move the points to the just found position, then rotate the character to fit the angle of the curve //(-90 is because the text is already vertical, it is as if it were already rotated 90 degrees) return(Matrix4x4.TRS(new Vector3(newMideBaselinePos.x, newMideBaselinePos.y, 0), Quaternion.AngleAxis(-Mathf.Atan2(y0, x0) * Mathf.Rad2Deg - 90, Vector3.forward), Vector3.one)); }
void OnDrawGizmos() { if (m_TextComponent == null) { m_TextComponent = GetComponent <TMP_Text>(); if (m_TextComponent == null) { return; } } m_Transform = m_TextComponent.transform; // Get a reference to the text object's textInfo m_TextInfo = m_TextComponent.textInfo; // Update Text Statistics ObjectStats = "Characters: " + m_TextInfo.characterCount + " Words: " + m_TextInfo.wordCount + " Spaces: " + m_TextInfo.spaceCount + " Sprites: " + m_TextInfo.spriteCount + " Links: " + m_TextInfo.linkCount + "\nLines: " + m_TextInfo.lineCount + " Pages: " + m_TextInfo.pageCount; // Get the handle size for drawing the various m_ScaleMultiplier = m_TextComponent.GetType() == typeof(TextMeshPro) ? 1 : 0.1f; m_HandleSize = HandleUtility.GetHandleSize(m_Transform.position) * m_ScaleMultiplier; // Draw line metrics #region Draw Lines if (ShowLines) { DrawLineBounds(); } #endregion // Draw word metrics #region Draw Words if (ShowWords) { DrawWordBounds(); } #endregion // Draw character metrics #region Draw Characters if (ShowCharacters) { DrawCharactersBounds(); } #endregion // Draw Quads around each of the words #region Draw Links if (ShowLinks) { DrawLinkBounds(); } #endregion // Draw Quad around the bounds of the text #region Draw Bounds if (ShowMeshBounds) { DrawBounds(); } #endregion // Draw Quad around the rendered region of the text. #region Draw Text Bounds if (ShowTextBounds) { DrawTextBounds(); } #endregion }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> IEnumerator AnimateVertexColors() { // We force an update of the text object since it would only be updated at the end of the frame. Ie. before this code is executed on the first frame. // Alternatively, we could yield and wait until the end of the frame when the text object will be generated. m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = m_TextComponent.textInfo; Matrix4x4 matrix; int loopCount = 0; hasTextChanged = true; // Create an Array which contains pre-computed Angle Ranges and Speeds for a bunch of characters. VertexAnim[] vertexAnim = new VertexAnim[1024]; for (int i = 0; i < 1024; i++) { vertexAnim[i].angleRange = Random.Range(10f, 25f); vertexAnim[i].speed = Random.Range(1f, 3f); } // Cache the vertex data of the text object as the Jitter FX is applied to the original position of the characters. TMP_MeshInfo[] cachedMeshInfo = textInfo.CopyMeshInfoVertexData(); Color32[] newVertexColors; int characterCount = textInfo.characterCount; for (int i = 0; i < characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; int materialIndex = charInfo.materialReferenceIndex; newVertexColors = textInfo.meshInfo[materialIndex].colors32; int vertexIndex = charInfo.vertexIndex; newVertexColors[vertexIndex + 0].a = 0; newVertexColors[vertexIndex + 1].a = 0; newVertexColors[vertexIndex + 2].a = 0; newVertexColors[vertexIndex + 3].a = 0; } while (true) { // If No Characters then just yield and wait for some text to be added if (characterCount == 0) { yield return(new WaitForSeconds(0.25f)); continue; } if (currentCharacter < characterCount) { int materialIndex = textInfo.characterInfo[currentCharacter].materialReferenceIndex; newVertexColors = textInfo.meshInfo[materialIndex].colors32; int vertexIndex = textInfo.characterInfo[currentCharacter].vertexIndex; newVertexColors[vertexIndex + 0].a = 255; newVertexColors[vertexIndex + 1].a = 255; newVertexColors[vertexIndex + 2].a = 255; newVertexColors[vertexIndex + 3].a = 255; if (currentCharacter + 1 < characterCount) { currentCharacter += 1; } m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); } for (int i = 0; i < characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; // Skip characters that are not visible and thus have no geometry to manipulate. //if (!charInfo.isVisible) //continue; // Retrieve the pre-computed animation data for the given character. VertexAnim vertAnim = vertexAnim[i]; // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Get the cached vertices of the mesh used by this text element (character or sprite). Vector3[] sourceVertices = cachedMeshInfo[materialIndex].vertices; // Determine the center point of each character at the baseline. //Vector2 charMidBasline = new Vector2((sourceVertices[vertexIndex + 0].x + sourceVertices[vertexIndex + 2].x) / 2, charInfo.baseLine); // 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; vertAnim.angle = Mathf.SmoothStep(-vertAnim.angleRange, vertAnim.angleRange, Mathf.PingPong(loopCount / 25f * vertAnim.speed, 1f)); Vector3 jitterOffset = new Vector3(Random.Range(-.25f, .25f), Random.Range(-.25f, .25f), 0); matrix = Matrix4x4.TRS(jitterOffset * CurveScale, Quaternion.Euler(0, 0, Random.Range(-5f, 5f) * 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]); destinationVertices[vertexIndex + 0] += offset; destinationVertices[vertexIndex + 1] += offset; destinationVertices[vertexIndex + 2] += offset; destinationVertices[vertexIndex + 3] += offset; vertexAnim[i] = vertAnim; } // Push changes into meshes for (int i = 0; i < textInfo.meshInfo.Length; i++) { textInfo.meshInfo[i].mesh.vertices = textInfo.meshInfo[i].vertices; m_TextComponent.UpdateGeometry(textInfo.meshInfo[i].mesh, i); } loopCount += 1; yield return(new WaitForSeconds(0.05f)); } }
IEnumerator StartFall() { allFalling = true; // Reset curChar value if all the characters had previously started their motion. if (curChar == characterCount) { curChar = 0; } // Set the curve wrap modes. This can (and should) be done in the inspector. fallCurve.preWrapMode = WrapMode.Clamp; fallCurve.postWrapMode = WrapMode.Clamp; bounceCurve.preWrapMode = WrapMode.Loop; bounceCurve.postWrapMode = WrapMode.Loop; // Generate the mesh and populate the textInfo with data we can use and manipulate. m_TextComponent.ForceMeshUpdate(); // Store textInfo and characterCount values. textInfo = m_TextComponent.textInfo; characterCount = textInfo.characterCount; // Get the index of the mesh used by this character. int matIndex = textInfo.characterInfo[curChar].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertIndex = textInfo.characterInfo[curChar].vertexIndex; Vector3[] verts; // Start fading in the elements. if (fadeInOnFall) { colorFadeScript.startFadeIn = true; } // Check the correct bools in the TMPTextColorFade script to make it have the correct order. if (randomOrder) { charOrder = handlerScript.randomOrder; } else { charOrder = handlerScript.leftRightOrder; } // Start randomly falling characters. while (curChar < characterCount) { // Instructing TextMesh Pro not to upload the mesh as we will be modifying it. m_TextComponent.renderMode = TextRenderFlags.DontRender; // Get the index of the mesh used by this character. matIndex = textInfo.characterInfo[charOrder[curChar]].materialReferenceIndex; // Get the index of the first vertex used by this text element. vertIndex = textInfo.characterInfo[charOrder[curChar]].vertexIndex; // Get the array of vertices used by this text element. verts = textInfo.meshInfo[matIndex].vertices; // Ignore "spaces" as they do not seem to have vertices they cause problems. // But it does count as a character so the delay between characters falling must still be applied. if (textInfo.characterInfo[charOrder[curChar]].character.ToString() != " ") { StartCoroutine(FallOnce(verts, matIndex, vertIndex, charOrder[curChar])); } curChar++; // Delay between every text character. yield return(new WaitForSeconds(timeBetweenChar)); } //allFalling = true; }
private void TmpTextOnOnPreRenderText(TMP_TextInfo obj) => rectTransform.sizeDelta = new Vector2(lineLenght, tmpText.textBounds.size.y);
/// <summary> /// Method that makes a character fall. /// </summary> /// <param name="textComponent"></param> /// <returns></returns> IEnumerator FallOnce(Vector3[] vertices, int materialIndex, int vertexIndex, int currentCharacter) { float timer = 0f; int bounceCount = 1; bool falling = true; bool bouncing = false; float originalY0 = vertices[vertexIndex + 0].y; float originalY1 = vertices[vertexIndex + 1].y; float originalY2 = vertices[vertexIndex + 2].y; float originalY3 = vertices[vertexIndex + 3].y; while (falling) { timer += Time.deltaTime / fallDur; // Compute the baseline mid point for each character float yOffset = (fallCurve.Evaluate(timer) * fallCurveScale); // Apply offset to adjust our pivot point. vertices[vertexIndex + 0].y = originalY0 + yOffset; vertices[vertexIndex + 1].y = originalY1 + yOffset; vertices[vertexIndex + 2].y = originalY2 + yOffset; vertices[vertexIndex + 3].y = originalY3 + yOffset; // Upload the mesh with the revised information m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Vertices); // Store textInfo values. textInfo = m_TextComponent.textInfo; // When to force update the vertices. updateAfterCount++; if (timer >= 1) { falling = false; if (bounceAfterFall) { bouncing = true; } else if (waveAfterFall && currentCharacter == handlerScript.leftRightOrder[characterCount - 1]) { waveScript.waveOn = true; } timer = 0f; } yield return(null); } while (bouncing) { timer += Time.deltaTime / bounceDur; if (timer >= 1f) { bounceCount++; timer = 0f; } // Compute the baseline mid point for each character float yOffset = ((bounceCurve.Evaluate(timer) * bounceCurveScale) / (bounceCount * bounceDampner)); // Apply offset to adjust our pivot point. vertices[vertexIndex + 0].y = originalY0 + yOffset; vertices[vertexIndex + 1].y = originalY1 + yOffset; vertices[vertexIndex + 2].y = originalY2 + yOffset; vertices[vertexIndex + 3].y = originalY3 + yOffset; // Upload the mesh with the revised information m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Vertices); // Store textInfo values. textInfo = m_TextComponent.textInfo; // When to force update the vertices. updateAfterCount++; if (bounceCount >= bounces) { bouncing = false; if (startFromMid) { curChar--; } } yield return(null); } }
/// <summary> /// Draw rectangle around each of the links contained in the text. /// </summary> /// <param name="text"></param> void DrawLinkBounds() { TMP_TextInfo textInfo = m_TextComponent.textInfo; for (int i = 0; i < textInfo.linkCount; i++) { TMP_LinkInfo linkInfo = textInfo.linkInfo[i]; bool isBeginRegion = false; Vector3 bottomLeft = Vector3.zero; Vector3 topLeft = Vector3.zero; Vector3 bottomRight = Vector3.zero; Vector3 topRight = Vector3.zero; float maxAscender = -Mathf.Infinity; float minDescender = Mathf.Infinity; Color32 linkColor = Color.cyan; // Iterate through each character of the link text for (int j = 0; j < linkInfo.linkTextLength; j++) { int characterIndex = linkInfo.linkTextfirstCharacterIndex + j; TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex]; int currentLine = currentCharInfo.lineNumber; bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters || currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines || (m_TextComponent.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; // Track Max Ascender and Min Descender maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender); minDescender = Mathf.Min(minDescender, currentCharInfo.descender); if (isBeginRegion == false && isCharacterVisible) { isBeginRegion = true; bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0); topLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0); //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]"); // If Link is one character if (linkInfo.linkTextLength == 1) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); } } // Last Character of Link if (isBeginRegion && j == linkInfo.linkTextLength - 1) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); } // If Link is split on more than one line. else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor); maxAscender = -Mathf.Infinity; minDescender = Mathf.Infinity; //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); } } //Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo)); } }
IEnumerator StartFallFromMid() { //int currentCharacter = 0; int currentCharacterLeft; int currentCharacterRight; space = 0; Vector3[] vertsLeft; Vector3[] vertsRight; Vector3[] verts; allFalling = true; // Reset curChar value if all the characters had previously started their motion. if (curChar >= characterCount) { curChar = 0; } // Set the curve wrap modes. This can (and should) be done in the inspector. fallCurve.preWrapMode = WrapMode.Clamp; fallCurve.postWrapMode = WrapMode.Clamp; bounceCurve.preWrapMode = WrapMode.Loop; bounceCurve.postWrapMode = WrapMode.Loop; // Generate the mesh and populate the textInfo with data we can use and manipulate. m_TextComponent.ForceMeshUpdate(); // Store textInfo and characterCount values. textInfo = m_TextComponent.textInfo; characterCount = textInfo.characterCount; // Start fading in the elements. if (fadeInOnFall) { colorFadeScript.startFadeIn = true; } // Assign the left to right order for fall to wave purposes. charOrder = handlerScript.leftRightOrder; // Check if even with modulo. // Even. if (characterCount % 2 == 0) { currentCharacterLeft = characterCount / 2 - 1; currentCharacterRight = currentCharacterLeft + 1; } // Odds. else { curChar = Mathf.CeilToInt(characterCount / 2); // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[curChar].materialReferenceIndex; // Get the array of vertices used by this text element. verts = textInfo.meshInfo[materialIndex].vertices; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[curChar].vertexIndex; if (textInfo.characterInfo[curChar].character.ToString() != " ") { StartCoroutine(FallOnce(verts, materialIndex, vertexIndex, curChar)); curChar++; } else { space++; } currentCharacterLeft = curChar - 1; currentCharacterRight = curChar + 1; } // Fade in the rest of the characters. To the Left and to the Right at the same time. while (currentCharacterRight < characterCount) { // For the character to the left. // Get the index of the material used by the current character. int materialIndexLeft = textInfo.characterInfo[currentCharacterLeft].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndexLeft = textInfo.characterInfo[currentCharacterLeft].vertexIndex; // Get the array of vertices used by this text element. vertsLeft = textInfo.meshInfo[materialIndexLeft].vertices; if (textInfo.characterInfo[currentCharacterLeft].character.ToString() != " ") { StartCoroutine(FallOnce(vertsLeft, materialIndexLeft, vertexIndexLeft, currentCharacterLeft)); curChar++; } else { space++; } currentCharacterLeft--; // For the character to the right. // Get the index of the material used by the current character. int materialIndexRight = textInfo.characterInfo[currentCharacterRight].materialReferenceIndex; // Get the index of the first vertex used by this text element. int vertexIndexRight = textInfo.characterInfo[currentCharacterRight].vertexIndex; // Get the array of vertices used by this text element. vertsRight = textInfo.meshInfo[materialIndexRight].vertices; if (textInfo.characterInfo[currentCharacterRight].character.ToString() != " ") { StartCoroutine(FallOnce(vertsRight, materialIndexRight, vertexIndexRight, currentCharacterRight)); curChar++; } else { space++; } currentCharacterRight++; yield return(new WaitForSeconds(timeBetweenChar)); } }
/// <summary> /// Draw Rectangles around each lines of the text. /// </summary> /// <param name="text"></param> void DrawLineBounds() { TMP_TextInfo textInfo = m_TextComponent.textInfo; for (int i = 0; i < textInfo.lineCount; i++) { TMP_LineInfo lineInfo = textInfo.lineInfo[i]; bool isLineVisible = (lineInfo.characterCount == 1 && textInfo.characterInfo[lineInfo.firstCharacterIndex].character == 10) || i > m_TextComponent.maxVisibleLines || (m_TextComponent.overflowMode == TextOverflowModes.Page && textInfo.characterInfo[lineInfo.firstCharacterIndex].pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; if (!isLineVisible) { continue; } //if (!ShowLinesOnlyVisibleCharacters) //{ // Get Bottom Left and Top Right position of each line float ascender = lineInfo.ascender; float descender = lineInfo.descender; float baseline = lineInfo.baseline; float maxAdvance = lineInfo.maxAdvance; Vector3 bottomLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, descender, 0)); Vector3 topLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, ascender, 0)); Vector3 topRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, ascender, 0)); Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, descender, 0)); DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, Color.green); Vector3 bottomOrigin = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin, descender, 0)); Vector3 topOrigin = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin, ascender, 0)); Vector3 bottomAdvance = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin + maxAdvance, descender, 0)); Vector3 topAdvance = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].origin + maxAdvance, ascender, 0)); DrawDottedRectangle(bottomOrigin, topOrigin, topAdvance, bottomAdvance, Color.green); Vector3 baselineStart = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstCharacterIndex].bottomLeft.x, baseline, 0)); Vector3 baselineEnd = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastCharacterIndex].topRight.x, baseline, 0)); Gizmos.color = Color.cyan; Gizmos.DrawLine(baselineStart, baselineEnd); // Draw LineExtents Gizmos.color = Color.grey; Gizmos.DrawLine(m_Transform.TransformPoint(lineInfo.lineExtents.min), m_Transform.TransformPoint(lineInfo.lineExtents.max)); //} //else //{ //// Get Bottom Left and Top Right position of each line //float ascender = lineInfo.ascender; //float descender = lineInfo.descender; //Vector3 bottomLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, descender, 0)); //Vector3 topLeft = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, ascender, 0)); //Vector3 topRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, ascender, 0)); //Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, descender, 0)); //DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, Color.green); //Vector3 baselineStart = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].bottomLeft.x, textInfo.characterInfo[lineInfo.firstVisibleCharacterIndex].baseLine, 0)); //Vector3 baselineEnd = m_Transform.TransformPoint(new Vector3(textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].topRight.x, textInfo.characterInfo[lineInfo.lastVisibleCharacterIndex].baseLine, 0)); //Gizmos.color = Color.cyan; //Gizmos.DrawLine(baselineStart, baselineEnd); //} } }
public override void ModifyCharacter(CharacterData characterData, TMP_Text textComponent, TMP_TextInfo textInfo, float progress, TMP_MeshInfo[] meshInfo) { if (colors == null || colors.Length == 0) { return; } var materialIndex = characterData.MaterialIndex; newVertexColors = textInfo.meshInfo[materialIndex].colors32; var vertexIndex = characterData.VertexIndex; var targetColor = colors[characterData.Index % colors.Length]; Color currentColor = newVertexColors[0]; targetColor = targetColor * curve.Evaluate(characterData.Progress); currentColor = currentColor * (1f - curve.Evaluate(characterData.Progress)); newVertexColors[vertexIndex + 0] = currentColor + targetColor; newVertexColors[vertexIndex + 1] = currentColor + targetColor; newVertexColors[vertexIndex + 2] = currentColor + targetColor; newVertexColors[vertexIndex + 3] = currentColor + targetColor; }
private IEnumerator AnimateVertexColors() { this.m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = this.m_TextComponent.textInfo; Vector3[][] copyOfVertices = new Vector3[0][]; this.hasTextChanged = true; for (;;) { if (this.hasTextChanged) { if (copyOfVertices.Length < textInfo.meshInfo.Length) { copyOfVertices = new Vector3[textInfo.meshInfo.Length][]; } for (int i = 0; i < textInfo.meshInfo.Length; i++) { int num = textInfo.meshInfo[i].vertices.Length; copyOfVertices[i] = new Vector3[num]; } this.hasTextChanged = false; } if (textInfo.characterCount == 0) { yield return(new WaitForSeconds(0.25f)); } else { int lineCount = textInfo.lineCount; for (int j = 0; j < lineCount; j++) { int firstCharacterIndex = textInfo.lineInfo[j].firstCharacterIndex; int lastCharacterIndex = textInfo.lineInfo[j].lastCharacterIndex; Vector3 b = (textInfo.characterInfo[firstCharacterIndex].bottomLeft + textInfo.characterInfo[lastCharacterIndex].topRight) / 2f; Quaternion q = Quaternion.Euler(0f, 0f, UnityEngine.Random.Range(-0.25f, 0.25f)); for (int k = firstCharacterIndex; k <= lastCharacterIndex; k++) { if (textInfo.characterInfo[k].isVisible) { int materialReferenceIndex = textInfo.characterInfo[k].materialReferenceIndex; int vertexIndex = textInfo.characterInfo[k].vertexIndex; Vector3[] vertices = textInfo.meshInfo[materialReferenceIndex].vertices; Vector3 b2 = (vertices[vertexIndex] + vertices[vertexIndex + 2]) / 2f; copyOfVertices[materialReferenceIndex][vertexIndex] = vertices[vertexIndex] - b2; copyOfVertices[materialReferenceIndex][vertexIndex + 1] = vertices[vertexIndex + 1] - b2; copyOfVertices[materialReferenceIndex][vertexIndex + 2] = vertices[vertexIndex + 2] - b2; copyOfVertices[materialReferenceIndex][vertexIndex + 3] = vertices[vertexIndex + 3] - b2; float d = UnityEngine.Random.Range(0.95f, 1.05f); Matrix4x4 matrix = Matrix4x4.TRS(Vector3.one, Quaternion.identity, Vector3.one * d); copyOfVertices[materialReferenceIndex][vertexIndex] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex]); copyOfVertices[materialReferenceIndex][vertexIndex + 1] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 1]); copyOfVertices[materialReferenceIndex][vertexIndex + 2] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 2]); copyOfVertices[materialReferenceIndex][vertexIndex + 3] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 3]); copyOfVertices[materialReferenceIndex][vertexIndex] += b2; copyOfVertices[materialReferenceIndex][vertexIndex + 1] += b2; copyOfVertices[materialReferenceIndex][vertexIndex + 2] += b2; copyOfVertices[materialReferenceIndex][vertexIndex + 3] += b2; copyOfVertices[materialReferenceIndex][vertexIndex] -= b; copyOfVertices[materialReferenceIndex][vertexIndex + 1] -= b; copyOfVertices[materialReferenceIndex][vertexIndex + 2] -= b; copyOfVertices[materialReferenceIndex][vertexIndex + 3] -= b; matrix = Matrix4x4.TRS(Vector3.one, q, Vector3.one); copyOfVertices[materialReferenceIndex][vertexIndex] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex]); copyOfVertices[materialReferenceIndex][vertexIndex + 1] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 1]); copyOfVertices[materialReferenceIndex][vertexIndex + 2] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 2]); copyOfVertices[materialReferenceIndex][vertexIndex + 3] = matrix.MultiplyPoint3x4(copyOfVertices[materialReferenceIndex][vertexIndex + 3]); copyOfVertices[materialReferenceIndex][vertexIndex] += b; copyOfVertices[materialReferenceIndex][vertexIndex + 1] += b; copyOfVertices[materialReferenceIndex][vertexIndex + 2] += b; copyOfVertices[materialReferenceIndex][vertexIndex + 3] += b; } } } for (int l = 0; l < textInfo.meshInfo.Length; l++) { textInfo.meshInfo[l].mesh.vertices = copyOfVertices[l]; this.m_TextComponent.UpdateGeometry(textInfo.meshInfo[l].mesh, l); } yield return(new WaitForSeconds(0.1f)); } } yield break; }
/// <summary> /// Method to draw rectangles around each word of the text. /// </summary> /// <param name="text"></param> void DrawWords(TextMeshProUGUI text) { TMP_TextInfo textInfo = text.textInfo; for (int i = 0; i < textInfo.wordCount; i++) { TMP_WordInfo wInfo = textInfo.wordInfo[i]; bool isBeginRegion = false; Vector3 bottomLeft = Vector3.zero; Vector3 topLeft = Vector3.zero; Vector3 bottomRight = Vector3.zero; Vector3 topRight = Vector3.zero; float maxAscender = -Mathf.Infinity; float minDescender = Mathf.Infinity; Color wordColor = Color.green; // Iterate through each character of the word for (int j = 0; j < wInfo.characterCount; j++) { int characterIndex = wInfo.firstCharacterIndex + j; TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex]; int currentLine = currentCharInfo.lineNumber; bool isCharacterVisible = characterIndex > text.maxVisibleCharacters || currentCharInfo.lineNumber > text.maxVisibleLines || (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true; // Track Max Ascender and Min Descender maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender); minDescender = Mathf.Min(minDescender, currentCharInfo.descender); if (isBeginRegion == false && isCharacterVisible) { isBeginRegion = true; bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0); topLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0); //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]"); // If Word is one character if (wInfo.characterCount == 1) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); } } // Last Character of Word if (isBeginRegion && j == wInfo.characterCount - 1) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); } // If Word is split on more than one line. else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber) { isBeginRegion = false; topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); // Draw Region DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); maxAscender = -Mathf.Infinity; minDescender = Mathf.Infinity; } } //Debug.Log(wInfo.GetWord(textInfo.characterInfo)); } }
/// <suMouseManager.Instanceary> /// Method to curve text along a Unity animation curve. /// </suMouseManager.Instanceary> /// <param name="textComponent"></param> /// <returns></returns> IEnumerator WarpText() { VertexCurve.preWrapMode = WrapMode.Clamp; VertexCurve.postWrapMode = WrapMode.Clamp; //Mesh mesh = m_TextComponent.textInfo.meshInfo[0].mesh; Vector3[] vertices; Matrix4x4 matrix; m_TextComponent.havePropertiesChanged = true; // Need to force the TextMeshPro Object to be updated. CurveScale *= 10; float old_CurveScale = CurveScale; AnimationCurve old_curve = CopyAnimationCurve(VertexCurve); while (true) { if (!m_TextComponent.havePropertiesChanged && old_CurveScale == CurveScale && old_curve.keys[1].value == VertexCurve.keys[1].value) { yield return(null); continue; } old_CurveScale = CurveScale; old_curve = CopyAnimationCurve(VertexCurve); m_TextComponent.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate. TMP_TextInfo textInfo = m_TextComponent.textInfo; int characterCount = textInfo.characterCount; if (characterCount == 0) { continue; } //vertices = textInfo.meshInfo[0].vertices; //int lastVertexIndex = textInfo.characterInfo[characterCount - 1].vertexIndex; float boundsMinX = m_TextComponent.bounds.min.x; //textInfo.meshInfo[0].mesh.bounds.min.x; float boundsMaxX = m_TextComponent.bounds.max.x; //textInfo.meshInfo[0].mesh.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); //float offsetY = VertexCurve.Evaluate((float)i / characterCount + loopCount / 50f); // Random.Range(-0.25f, 0.25f); // 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 float x0 = (offsetToMidBaseline.x - boundsMinX) / (boundsMaxX - boundsMinX); // Character's position relative to the bounds of the mesh. float x1 = x0 + 0.0001f; float y0 = VertexCurve.Evaluate(x0) * CurveScale; float y1 = VertexCurve.Evaluate(x1) * CurveScale; Vector3 horizontal = new Vector3(1, 0, 0); //Vector3 normal = new Vector3(-(y1 - y0), (x1 * (boundsMaxX - boundsMinX) + boundsMinX) - offsetToMidBaseline.x, 0); Vector3 tangent = new Vector3(x1 * (boundsMaxX - boundsMinX) + boundsMinX, y1) - new Vector3(offsetToMidBaseline.x, y0); float dot = Mathf.Acos(Vector3.Dot(horizontal, tangent.normalized)) * 57.2957795f; 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 m_TextComponent.UpdateVertexData(); yield return(new WaitForSeconds(0.025f)); } }
/// <summary> /// Method to animate vertex colors of a TMP Text object. /// </summary> /// <returns></returns> IEnumerator AnimateVertexColors() { yield return(new WaitForSeconds(3f)); // Need to force the text object to be generated so we have valid data to work with right from the start. m_TextComponent.ForceMeshUpdate(); TMP_TextInfo textInfo = m_TextComponent.textInfo; Color32[] newVertexColors; int currentCharacter = 0; int startingCharacterRange = currentCharacter; bool isRangeMax = false; while (!isRangeMax) { int characterCount = textInfo.characterCount; // Spread should not exceed the number of characters. byte fadeSteps = (byte)Mathf.Max(1, 255 / RolloverCharacterSpread); for (int i = startingCharacterRange; i < currentCharacter + 1; i++) { // Skip characters that are not visible if (!textInfo.characterInfo[i].isVisible) { continue; } // Get the index of the material used by the current character. int materialIndex = textInfo.characterInfo[i].materialReferenceIndex; // Get the vertex colors of the mesh used by this text element (character or sprite). newVertexColors = textInfo.meshInfo[materialIndex].colors32; // Get the index of the first vertex used by this text element. int vertexIndex = textInfo.characterInfo[i].vertexIndex; // Get the current character's alpha value. byte alpha = (byte)Mathf.Clamp(newVertexColors[vertexIndex + 0].a - fadeSteps, 0, 255); // Set new alpha values. newVertexColors[vertexIndex + 0].a = alpha; newVertexColors[vertexIndex + 1].a = alpha; newVertexColors[vertexIndex + 2].a = alpha; newVertexColors[vertexIndex + 3].a = alpha; // Tint vertex colors // Note: Vertex colors are Color32 so we need to cast to Color to multiply with tint which is Color. newVertexColors[vertexIndex + 0] = (Color)newVertexColors[vertexIndex + 0] * ColorTint; newVertexColors[vertexIndex + 1] = (Color)newVertexColors[vertexIndex + 1] * ColorTint; newVertexColors[vertexIndex + 2] = (Color)newVertexColors[vertexIndex + 2] * ColorTint; newVertexColors[vertexIndex + 3] = (Color)newVertexColors[vertexIndex + 3] * ColorTint; if (alpha == 0) { startingCharacterRange += 1; if (startingCharacterRange == characterCount) { // Update mesh vertex data one last time. m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); yield return(new WaitForSeconds(1.0f)); // Reset the text object back to original state. m_TextComponent.ForceMeshUpdate(); yield return(new WaitForSeconds(1.0f)); // Reset our counters. currentCharacter = 0; startingCharacterRange = 0; //isRangeMax = true; // Would end the coroutine. } } } // Upload the changed vertex colors to the Mesh. m_TextComponent.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32); if (currentCharacter + 1 < characterCount) { currentCharacter += 1; } yield return(new WaitForSeconds(0.25f - FadeSpeed * 0.01f)); } }