Search Unity

TextMesh Pro Trying to Make Text Selectable

Discussion in 'UGUI & TextMesh Pro' started by MrHappyKiller, Jul 20, 2017.

  1. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    So, I currently have my game load a list of saves to a text object and split them with some line spacing:

    Code (CSharp):
    1.         public void LoadSaveList()
    2.         {
    3.             var saveList = Directory.GetFiles(Constants.SAVEDIR);
    4.             var finalSaveString = new StringBuilder();
    5.  
    6.             if (saveList.Any())
    7.             {
    8.                 foreach (var s in saveList)
    9.                 {
    10.                     string fileName = s.Substring(66);
    11.  
    12.                     if (finalSaveString.Length <= 0)
    13.                     {
    14.                         finalSaveString.Append(fileName);
    15.                     }
    16.  
    17.                     finalSaveString.Append("\n" + fileName);
    18.                 }
    19.  
    20.                 saveText.text = finalSaveString.ToString();
    21.  
    22.                 return;
    23.             }
    24.  
    25.             saveText.text = "No saves found.";
    26.         }

    And now I am completely stuck on how I would be able to click one of the Save file names and set it as selected (change color or put a text box around it). I haven't tried anything yet, because I'm lost on where to begin. :/
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    There are a few ways this could be implemented.

    First I suggest you look at example 12a and related scripts included with TextMesh Pro. This uses event handlers to detect interactions with characters, words, lines, links, etc.

    One way that you could implement this is to append a link tag to each line to be saved / interacted with. Ie. <link="SAVE_01">Whatever text you want to show up</link>. The "SAVE_01" is a unique ID that you set to enable you to easily know what link is being interacted with so you can implement whatever logic you want.

    An alternative way could be to instantiate buttons without images / graphics in the position of the lines of text. The logic would then have to be implemented on each button.

    To position the buttons, you could use the information contained in the TMP_Text.textInfo class. What is that?

    When a text object is processed, the TMP_Text.textInfo is populated with information about the text object, characters, words, lines, etc. This is what those event handler scripts in example 12a and rely on to know the position of characters, words, etc.

    To help visualize this information, you can place a utility script called "TMP_TextInfoDebugTool.cs" on any TMP text object. This will allows you to show characters, words, lines, etc.

    Using the information contained in the textInfo, you could get the first and last character of each line by accessing the lineInfo[lineIndex] then with the index of those characters look up their position in the characterInfo[index] and use that to size and place the button.

    There are other ways this could be done but these two should work fine.
     
  3. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    This has really helped. :) One question though. Could you point me to some documentation or a tutorial on how to manipulate links in script? (such us doing something when hovering, clicking, etc). I'll probably find it on my own, and I am looking now. But having a reference might speed it up. :p Thanks again!
     
  4. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Ok, kind of got it to work (slightly). For some reason, the links don't line up properly in game. They seem to be above the text object even though they are shown on it in the scene view through debugging. I am using the built in Unity Content Size Fitter (couldn't find a different one in TMP). Would that maybe be the problem?
     
  5. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Also, I have eleven files. It fully populates the link info with those ten, but then adds five more blank linkinfo objects to the list for what I can see is no reason at all. :/
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    This is an old video which is should provide some insight on some of this. Although the implementation changed somewhat with the introduction of the event based handling used in example 12a, how this is done behind the scene remains valid.

    In terms of the linkInfo, the array is allocated in blocks so checking the length of the array doesn't match the number of links. For that you need to check textInfo.linkCount.

    In terms of links lining up or not, take a look again at example 12a and the TextInfoDebugTool.cs script attached on the object. By enabling / disabling showing different options, you can verify if the tracking of the characters, words, lines or links is correct. You can also look at the script to understand how it draws these bounds and handles the fact the information is in object / local space.

    Provided you are using the TextMeshProUGUI component located in UI - TextMeshPro - Text, all layout components should work as expected.
     
  7. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Took a re look over it and realized I had kept Unity open too long so it was doing weird things. The links are set correctly! Hurray! But, for some reason the character positions are off. I pass my linkIndex gathered from "TMP_TextUtilities.FindIntersectingLink" into my linkInfo array of my text object. Then I pass linkInfo.linkTextfirstCharacterIndex into the characterInfo array of my text object. This correctly gets what character it is. But, when using "characterInfo.bottomLeft, .topRight, etc." it puts it pretty far off from where the char actually is. I'm guessing it's using world space instead of local space perhaps? Here's my code:

    Code (CSharp):
    1.     void UpdateSelectedLink()
    2.     {
    3.         if (GetCurrentLinkIndex() > -1)
    4.         {
    5.             var linkInfo = saveText.textInfo.linkInfo[GetCurrentLinkIndex()];
    6.  
    7.             var firstChar = saveText.textInfo.characterInfo[linkInfo.linkTextfirstCharacterIndex];
    8.  
    9.             var bottomLeft = firstChar.bottomLeft;
    10.             var topRight = firstChar.topRight;
    11.  
    12.             print(string.Format("BottomLeft: {0}, TopRight: {1}", bottomLeft, topRight));
    13.  
    14.             Gizmos.color = Color.cyan;
    15.             Gizmos.DrawLine(bottomLeft, topRight); //Yes, this is being called in OnDrawGizmos
    16.         }
    17.     }
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The reported positions are in object space. Take a look at that TextInfoDebugTool.cs script to see how it takes into consideration the transform position of the object.

    Again, take a look at Example 12a which uses the event handler as this might be an easier implementation as you will only need to check what link was clicked without having to worry about where it is unless, you are trying to draw stuff.
     
  9. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Yes, I actually realized my mistake after looking through the example. I do want to (later on in development) draw a selection box around the line when the link is being intersected. But for now, I would just like to change the color of the text on that one line. I haven't been able to find a way to do that. :/ I did try using color tags, but line info does not seem to let me change the text of one line at a time
     
  10. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The content of the textInfo simply provides information about the text object. Changing its content doesn't have any affect on the text object itself. Ie. the textInfo is not an input.

    To modify vertex colors of certain characters, words, lines, etc. take a look at the AnimatingVertexAttribute example and related scripts. By using the textInfo to find what vertices and vertex colors a given character uses, you can then change its vertex colors for instance. This is what those examples show.
     
  11. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    I've got that slightly working. I can change from the highlighted color back to the original just fine when hovering over, then hovering away respectively. But, when I hover over any other line, it just changes the colors of the very first line. I've been looking through a few examples, but can't seem to find anything that addresses it.
     
  12. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Be mindful of characters that are not visible as their reported index would be zero which would could result in the wrong vertices and colors to be changed.

    Look also at example 12 and use the left or right shift key when mousing over characters.
     
  13. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    I do check if the char is visible before doing anything. You've helped me a lot, and I apologize for not fully understanding this. Here's how I'm handling it:

    Code (CSharp):
    1.     void SetSelectedLink(int linkIndex)
    2.     {
    3.         selectedLinkIndex = linkIndex;
    4.  
    5.         if (linkIndex > -1 && prevLinkIndex == -1 || linkIndex > -1 && hasRecolored)
    6.         {
    7.             prevLinkIndex = linkIndex;
    8.         }
    9.  
    10.         if (linkIndex > -1)
    11.         {
    12.             Color32[] newVertexColors;
    13.             Color32 color = saveText.color;
    14.  
    15.             if (hasRecolored || prevColor.Equals(new Color32(0, 0, 0, 0)))
    16.             {
    17.                 prevColor = color;
    18.  
    19.                 hasRecolored = false;
    20.             }
    21.  
    22.             var textInfo = saveText.textInfo;
    23.             var charInfo = textInfo.characterInfo;
    24.  
    25.             var lineInfo = textInfo.lineInfo[GetCurrentLinkIndex()];
    26.             var lastChar = charInfo[lineInfo.lastCharacterIndex - 1];
    27.  
    28.             for (int i = 0; i < lineInfo.characterCount; i++)
    29.             {
    30.                 int materialIndex = charInfo[i].materialReferenceIndex;
    31.                 int vertexIndex = charInfo[i].vertexIndex;
    32.  
    33.                 newVertexColors = textInfo.meshInfo[materialIndex].colors32;
    34.  
    35.                 if (charInfo[i].isVisible)
    36.                 {
    37.                     color = Color.green;
    38.  
    39.                     newVertexColors[vertexIndex + 0] = color;
    40.                     newVertexColors[vertexIndex + 1] = color;
    41.                     newVertexColors[vertexIndex + 2] = color;
    42.                     newVertexColors[vertexIndex + 3] = color;
    43.  
    44.                     saveText.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
    45.                 }
    46.             }
    47.         }
    48.         else
    49.         {
    50.             Color32[] newVertexColors;
    51.             Color32 color = prevColor;
    52.  
    53.             var textInfo = saveText.textInfo;
    54.             var charInfo = textInfo.characterInfo;
    55.  
    56.             var lineInfo = textInfo.lineInfo[prevLinkIndex];
    57.  
    58.             for (int i = 0; i < lineInfo.characterCount; i++)
    59.             {
    60.                 int materialIndex = charInfo[i].materialReferenceIndex;
    61.                 int vertexIndex = charInfo[i].vertexIndex;
    62.  
    63.                 newVertexColors = textInfo.meshInfo[materialIndex].colors32;
    64.  
    65.                 if (charInfo[i].isVisible)
    66.                 {
    67.                     newVertexColors[vertexIndex + 0] = color;
    68.                     newVertexColors[vertexIndex + 1] = color;
    69.                     newVertexColors[vertexIndex + 2] = color;
    70.                     newVertexColors[vertexIndex + 3] = color;
    71.  
    72.                     saveText.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
    73.                 }
    74.             }
    75.         }
    76.     }
    When I look and compare my code with the example, I don't see where this could be going wrong. It correctly identifies character counts of the lines so I know it's not anything to do with my link intersecting wrong. Am I gathering the vertex information wrong some how?
     
  14. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    When you do something like
    Code (csharp):
    1. int vertexIndex = charInfo[i].vertexIndex;
    Assuming i = 0, you are getting a reference to the vertices used by the character at index = 0;

    So if you want to change the colors of the characters on a given line, you need to get the vertex index of the first character of that line. Then you iterate through each character of that line (filtering out characters that are not visible like spaces as they have no geometry and would all reference index 0 which would keep resetting the first character which is typically at vertices index 0, 1, 2, 3

    Your script right now appears to be changing the vertex colors based on the index of a link? Are you trying to color based on links or lines?

    You are also changing the vertex color from the first character (index 0) up to the number of character of a line? You should be changing the vertex colors of the characters of a link or line but you keep going from characters 0 to some number of characters of a given line.
     
  15. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Hm, I was using the link index as a way to identify the lines considering that each new line is a new link. Are you saying I should just only work with links and get the link text to change it?
     
  16. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Wait, just got what you said. So I keep starting from the index of zero on my text object. So it never goes down the lines to find the correct one. Alright let me give it a shot
     
  17. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    So I got exactly what you were saying. I am now getting the correct index of the char and iterating over it:

    Code (CSharp):
    1.             var textInfo = saveText.textInfo;
    2.             var charInfo = textInfo.characterInfo;
    3.  
    4.             var linkInfo = textInfo.linkInfo[GetCurrentLinkIndex()];
    5.  
    6.             int currentChar = linkInfo.linkTextfirstCharacterIndex;
    7.  
    8.             for (int i = currentChar; i < linkInfo.linkTextLength; i++)
    9.             {
    10.                 print("Ran");
    11.  
    12.                 int materialIndex = charInfo[i].materialReferenceIndex;
    13.                 int vertexIndex = charInfo[i].vertexIndex;
    14.  
    15.                 newVertexColors = textInfo.meshInfo[materialIndex].colors32;
    16.  
    17.                 if (charInfo[i].isVisible)
    18.                 {
    19.                     color = Color.green;
    20.  
    21.                     newVertexColors[vertexIndex + 0] = color;
    22.                     newVertexColors[vertexIndex + 1] = color;
    23.                     newVertexColors[vertexIndex + 2] = color;
    24.                     newVertexColors[vertexIndex + 3] = color;
    25.  
    26.                     saveText.UpdateVertexData(TMP_VertexDataUpdateFlags.Colors32);
    27.                 }
    28.             }
    My problem now is that I'm comparing the linkTextLength to the currentChar. I need to compare it to the last character's index on the line but linkInfo has no method of getting that.
     
  18. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The linkInfo contains the linkTextFirstCharacterIndex and the linkTextLength. Therefore the last character of that link would be linkTextFirstCharacterIndex + linkTextLength;
     
  19. MrHappyKiller

    MrHappyKiller

    Joined:
    Feb 8, 2016
    Posts:
    29
    Yes, I'm disappointed I forgot the basics of strings. I did get it working. :) Now I just have to mess around with not letting things be selected when they are off screen, and I'll be set!
     
  20. HernandoNJ

    HernandoNJ

    Joined:
    May 13, 2018
    Posts:
    75