Search Unity

TextMesh Pro How to add underline to single character?

Discussion in 'UGUI & TextMesh Pro' started by rasicz, Dec 8, 2020.

  1. rasicz

    rasicz

    Joined:
    Apr 26, 2020
    Posts:
    2
    I'm trying to underline only part of text without using <u>tags. I found how to do it with colors, is there way how to do this also with underline?

    this is my current code:
    Code (CSharp):
    1.  
    2. int meshIndex = textField.textInfo.characterInfo[i].materialReferenceIndex;
    3. int vertexIndex = textField.textInfo.characterInfo[i].vertexIndex;
    4.  
    5. textField.textInfo.characterInfo[i].underlineVertexIndex = i * 4;
    6. Color32[] vertexColors = textField.textInfo.meshInfo[meshIndex].colors32;
    7. vertexColors[vertexIndex + 0] = color;
    8. vertexColors[vertexIndex + 1] = color;
    9. vertexColors[vertexIndex + 2] = color;
    10. vertexColors[vertexIndex + 3] = color;
    11.  
    12. //add underline
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    To change colors, we are simply changing the vertex color part of the character's geometry. For underline however, we need to actually create new geometry with the correct scale and position for this underline. As such, this is more complicated and can't be achieve by simply modifying the textInfo.

    The easiest way to achieve this might be for you to create your own underline geometry and to use the information contained in the textInfo and characterInfo to figure out where to position it.

    Here is an old post script (which may no longer work) but should help you get a better understanding of what I am suggesting. Note that in this case, this was used to add wavy lines under words for spell checking type use.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5.  
    6. namespace TMPro.Examples
    7. {
    8.    // Example script to simulate spell checking
    9.    public class WordHighlighting : MonoBehaviour
    10.    {
    11.        public Texture HightlightTexture;
    12.        public float HightlightSize = 5;
    13.        public float HightlightHeight = 5;
    14.  
    15.        private TMP_Text m_TextComponent;
    16.  
    17.        private GameObject m_WordHighlighter;
    18.  
    19.        private Mesh m_Mesh;
    20.        private TMP_MeshInfo m_MeshInfo;
    21.  
    22.        private MeshFilter m_MeshFilter;
    23.        private MeshRenderer m_MeshRenderer;
    24.  
    25.        private CanvasRenderer m_CanvasRenderer;
    26.  
    27.        private Material m_WavyLineMaterial;
    28.  
    29.        // Static text with array containing misspelled words.
    30.        private string m_SourceText = "This is an examle script to demonstrte how words could be highlightd to show misspeled words in a text object.";
    31.        private int[] m_misspelledWords = new int[] { 4, 7, 12, 15 };
    32.  
    33.  
    34.        void Awake()
    35.        {
    36.            m_TextComponent = GetComponent<TMP_Text>();
    37.            m_TextComponent.text = m_SourceText;
    38.  
    39.  
    40.            if (m_Mesh == null)
    41.            {
    42.                m_Mesh = new Mesh();
    43.                m_MeshInfo = new TMP_MeshInfo(m_Mesh, 8);
    44.            }
    45.  
    46.  
    47.            // Create object that will hold the geometry used to highlight misspelled words.
    48.            if (m_WordHighlighter == null)
    49.            {
    50.                m_WordHighlighter = new GameObject();
    51.                m_WordHighlighter.transform.SetParent(this.transform, false);
    52.  
    53.                // Create new material which supports tiling and assign the wavy line texture to it.
    54.                m_WavyLineMaterial = new Material(Shader.Find("TMPro/Sprite"));
    55.                m_WavyLineMaterial.SetTexture(ShaderUtilities.ID_MainTex, HightlightTexture);
    56.  
    57.                // Determine the type of TMP component
    58.                if (m_TextComponent as TextMeshPro != null)
    59.                {
    60.                    // Add the required components for the object type.
    61.                    m_MeshRenderer = m_WordHighlighter.AddComponent<MeshRenderer>();
    62.                    m_MeshRenderer.sharedMaterial = m_WavyLineMaterial;
    63.  
    64.                    m_MeshFilter = m_WordHighlighter.AddComponent<MeshFilter>();
    65.                    m_MeshFilter.mesh = m_Mesh;
    66.                }
    67.                else if (m_TextComponent as TextMeshProUGUI != null)
    68.                {
    69.                    m_CanvasRenderer = m_WordHighlighter.AddComponent<CanvasRenderer>();
    70.                    m_CanvasRenderer.SetMaterial(m_WavyLineMaterial, HightlightTexture);
    71.                }
    72.            }
    73.        }
    74.  
    75.  
    76.        void OnEnable()
    77.        {
    78.            // Subscribe to event fired when text object has been regenerated.
    79.            TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
    80.        }
    81.  
    82.  
    83.        void OnDisable()
    84.        {
    85.            TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
    86.        }
    87.  
    88.  
    89.        void OnDestroy()
    90.        {
    91.            if (m_Mesh != null)
    92.                DestroyImmediate(m_Mesh);
    93.        }
    94.  
    95.  
    96. #if UNITY_EDITOR
    97.        void OnValidate()
    98.        {
    99.            if (m_MeshRenderer != null && m_WavyLineMaterial != null && HightlightTexture != null)
    100.            {
    101.                m_WavyLineMaterial.SetTexture(ShaderUtilities.ID_MainTex, HightlightTexture);
    102.                ON_TEXT_CHANGED(m_TextComponent);
    103.            }
    104.  
    105.            if (m_CanvasRenderer != null && m_WavyLineMaterial != null && HightlightTexture != null)
    106.            {
    107.                m_CanvasRenderer.SetMaterial(m_WavyLineMaterial, HightlightTexture);
    108.                ON_TEXT_CHANGED(m_TextComponent);
    109.            }
    110.        }
    111. #endif
    112.  
    113.  
    114.        /// <summary>
    115.        /// Event called when a text object has been updated.
    116.        /// </summary>
    117.        /// <param name="obj">Object.</param>
    118.        void ON_TEXT_CHANGED(Object obj)
    119.        {
    120.            if (obj != m_TextComponent || m_WordHighlighter == null)
    121.                return;
    122.  
    123.            int wordCount = m_TextComponent.textInfo.wordCount;
    124.  
    125.            TMP_TextInfo textInfo = m_TextComponent.textInfo;
    126.  
    127.            // Add function to check source text to find misspelled words.
    128.            // For this example, the input text is static and I am using an array to indicate which words
    129.            // are misspelled.
    130.  
    131.            int numberOfMisspelledWords = m_misspelledWords.Length;
    132.  
    133.            // Make sure our Mesh allocations can hold the required geometry.
    134.            if (m_MeshInfo.vertices == null)
    135.                m_MeshInfo = new TMP_MeshInfo(m_Mesh, numberOfMisspelledWords + 1);
    136.            else if (m_MeshInfo.vertices.Length < numberOfMisspelledWords * 4)
    137.                m_MeshInfo.ResizeMeshInfo(Mathf.NextPowerOfTwo(numberOfMisspelledWords + 1));
    138.  
    139.            // Clear current geometry
    140.            m_MeshInfo.Clear(true);
    141.  
    142.            for (int i = 0; i < m_misspelledWords.Length; i++)
    143.            {
    144.                if (m_misspelledWords[i] > wordCount)
    145.                    continue;
    146.  
    147.                int first = textInfo.wordInfo[m_misspelledWords[i] - 1].firstCharacterIndex;
    148.                int last = textInfo.wordInfo[m_misspelledWords[i] - 1].lastCharacterIndex;
    149.                float wordScale = textInfo.characterInfo[first].scale;
    150.  
    151.                // Define a quad from first to last character of the word.
    152.                Vector3 bl = new Vector3(textInfo.characterInfo[first].bottomLeft.x, textInfo.characterInfo[first].baseLine - HightlightHeight * wordScale, 0);
    153.                Vector3 tl = new Vector3(bl.x, textInfo.characterInfo[first].baseLine, 0);
    154.                Vector3 tr = new Vector3(textInfo.characterInfo[last].topRight.x, tl.y, 0);
    155.                Vector3 br = new Vector3(tr.x, bl.y, 0);
    156.  
    157.                int index_X4 = i * 4;
    158.  
    159.                Vector3[] vertices = m_MeshInfo.vertices;
    160.                vertices[index_X4 + 0] = bl;
    161.                vertices[index_X4 + 1] = tl;
    162.                vertices[index_X4 + 2] = tr;
    163.                vertices[index_X4 + 3] = br;
    164.  
    165.                Vector2[] uvs0 = m_MeshInfo.uvs0;
    166.                float length = Mathf.Abs(tr.x - bl.x) / wordScale * HightlightSize;
    167.                float tiling = length / (HightlightTexture == null ? 1 : HightlightTexture.width);
    168.  
    169.                uvs0[index_X4 + 0] = new Vector2(0, 0);
    170.                uvs0[index_X4 + 1] = new Vector2(0, 1);
    171.                uvs0[index_X4 + 2] = new Vector2(tiling, 1);
    172.                uvs0[index_X4 + 3] = new Vector2(tiling, 0);
    173.            }
    174.  
    175.            // Push changes into meshes
    176.            m_Mesh.vertices = m_MeshInfo.vertices;
    177.            m_Mesh.uv = m_MeshInfo.uvs0;
    178.            m_Mesh.RecalculateBounds();
    179.  
    180.            // The canvas system requires using SetMesh whenever changes to the geometry are made.
    181.            if (m_TextComponent as TextMeshProUGUI != null)
    182.                m_CanvasRenderer.SetMesh(m_Mesh);
    183.  
    184.        }
    185.    }
    186. }
    187.  
     
    assertor and rasicz like this.
  3. rasicz

    rasicz

    Joined:
    Apr 26, 2020
    Posts:
    2
    Thanks! That worked for me.
     
  4. Rewaken

    Rewaken

    Joined:
    Mar 24, 2015
    Posts:
    128
    Hello, I'm kinda struggling with it is there any other way to add underline runtime?
    I made a list and( replacing selected word ) with (word and underline tag) to underline them it works but sometimes the integer value of TMP_TextUtilities.FindIntersectingWord gives the wrong output. Can you explain your code in more detail for example how can I underline a word when I click on it. Also I tried using your code but nothing happen and textInfo.characterInfo.underlineColor is not working