[Unity] UI Text with clickable words

Discussion in 'Game Development (Technical)' started by Scoper, Jan 5, 2017.

  1. Scoper

    Indie Author

    Joined:
    Nov 5, 2015
    Messages:
    82
    Likes Received:
    19
    In Unity 5.5, I need to display some text to the player with certain words highlighted in blue. If the player clicks one of these words the game displays the explanation for that word.

    Any ideas on how I could achieve that?
    The Unity UI.Text component allows rich text including text color, but it does not allow html-style links or anything similar.
    I also checked out the Unity Asset Store, but could find nothing useful.
     
    #1 Scoper, Jan 5, 2017
    Last edited: Jan 5, 2017
  2. Scoper

    Indie Author

    Joined:
    Nov 5, 2015
    Messages:
    82
    Likes Received:
    19
    I found a solution. Someone made a subclass of the Text component that supports clickable links. http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/#post-1753530
    I had to modify it to get it to work, but the essence is quite simple: Get the cachedTextGenerator.verts property of Text and compare mouse position to the vertices of characters in the string. The mouse position in the text recttransform can be calculated with RectTransformUtility.ScreenPointToLocalPointInRectangle and scaled to compensate for CanvasScaler. That allows me to test if the mouse is over a keyword.
     
  3. pima

    pima New Member

    Joined:
    Nov 28, 2018
    Messages:
    1
    Likes Received:
    0
    to fix the issue in new unity you should replace function onFillVBO in TextWithEvents script with this function


    public Mesh mesh;
    public float a;
    public float b;
    public float c;

    public float d;

    //regenerate interactable area every something critical changed like rescale, rotate, change text etc.
    //[Obsolete("Use OnPopulateMesh(VertexHelper vh) instead.", false)]
    protected override void OnPopulateMesh(VertexHelper vh)
    {
    Debug.Log(charsIdForClass.Count + " chars ids for cclass");
    if (mesh == null)
    {
    mesh = new Mesh();
    GetComponent<CanvasRenderer>().SetMesh(mesh);
    }

    base.OnPopulateMesh(vh);

    List<UIVertex> verts = new List<UIVertex>();

    float wHalf = rectTransform.rect.width / 2;
    float hHalf = rectTransform.rect.height / 2;
    a = Math.Min(1, Math.Max(0, a));
    b = Math.Min(1, Math.Max(0, b));
    c = Math.Min(1, Math.Max(0, c));
    d = Math.Min(1, Math.Max(0, d));

    Color32 color32 = color;

    vh.AddVert(new Vector3(-wHalf * a, 0), color32, new Vector2(0f, 0f));
    vh.AddVert(new Vector3(0, wHalf * b), color32, new Vector2(0f, 1f));
    vh.AddVert(new Vector3(wHalf * c, 0), color32, new Vector2(1f, 1f));
    vh.AddVert(new Vector3(0, -wHalf * d), color32, new Vector2(1f, 0f));

    vh.AddTriangle(0, 1, 2);
    vh.AddTriangle(2, 3, 0);

    vh.FillMesh(mesh);

    if (charsIdForClass.Count == 0)
    return;
    if (onlyColorChanged && areaForClass.Count > 0)
    {
    onlyColorChanged = false;
    return;
    }

    var cTextGen = cachedTextGenerator;
    //check if UI is normal or layout and alias them
    if (cTextGen == null)
    cTextGen = cachedTextGeneratorForLayout;
    foreach (var indexes in charsIdForClass)
    {
    //prepare event area for button like click, hover etc.
    if (areaForClass.ContainsKey(indexes.Key))
    areaForClass[indexes.Key].Clear();
    else
    areaForClass.Add(indexes.Key, new List<HashSet<Rect>>());
    int i = 0;
    //upewnij sie ze jest przerwa m liniami inaczej statetsy dziwnie dzialaja jak sa obok sie
    //support multiple link with same href
    foreach (var charsIndex in indexes.Value)
    {
    areaForClass[indexes.Key].Add(new HashSet<Rect>());
    //iteruj na liniach tylko raz o ile to mozliwe
    //iterate over text's lines if last char in link is under first char then generate rect for every line in link
    //or - if aggresive approx enable - max 3 rects
    if (cTextGen.lineCount > 1 && cTextGen.verts[charsIndex[0] * 4 + 2].position.y > cTextGen
    .verts[
    charsIndex[1] * 4 + 3 > cTextGen.vertexCount - 1
    ? cTextGen.vertexCount - 4
    : charsIndex[1] * 4].position.y)
    {
    for (var lId = 0; lId < cTextGen.lineCount - 1; lId++)
    {
    if (charsIndex[0] > cTextGen.lines[lId].startCharIdx &&
    charsIndex[0] < cTextGen.lines[lId + 1].startCharIdx)
    {
    areaForClass[indexes.Key].Add(new Rect(cTextGen.verts[charsIndex[0] * 4].position.x,
    cTextGen.verts[charsIndex[0] * 4 + 3].position.y,
    cTextGen.verts[(cTextGen.lines[lId + 1].startCharIdx - 1) * 4 + 1].position.x -
    cTextGen.verts[charsIndex[0] * 4].position.x, cTextGen.lines[lId].height));
    }
    else if (charsIndex[1] > cTextGen.lines[lId].startCharIdx &&
    charsIndex[1] < cTextGen.lines[lId + 1].startCharIdx)
    {
    areaForClass[indexes.Key].Add(new Rect(cTextGen.verts[charsIndex[1] * 4].position.x,
    cTextGen.verts[charsIndex[1] * 4 + 3].position.y,
    cTextGen.verts[charsIndex[1] * 4 + 1].position.x -
    cTextGen.verts[cTextGen.lines[lId].startCharIdx * 4].position.x,
    cTextGen.lines[lId].height));
    break;
    }
    else if (charsIndex[0] < cTextGen.lines[lId + 1].startCharIdx)
    {
    areaForClass[indexes.Key].Add(new Rect(
    cTextGen.verts[cTextGen.lines[lId].startCharIdx * 4].position.x,
    cTextGen.verts[cTextGen.lines[lId].startCharIdx * 4 + 3].position.y,
    cTextGen.verts[(cTextGen.lines[lId + 1].startCharIdx - 1) * 4 + 1].position.x -
    cTextGen.verts[cTextGen.lines[lId].startCharIdx * 4].position.x,
    cTextGen.lines[lId].height));
    }

    if (lId == cTextGen.lineCount - 2)
    {
    //check if ugui cut last vertices due to fact that text is too long for container
    if (charsIndex[1] * 4 + 3 > cTextGen.vertexCount - 1)
    areaForClass[indexes.Key].Add(new Rect(
    cTextGen.verts[cTextGen.lines[lId + 1].startCharIdx * 4].position.x,
    cTextGen.verts[cTextGen.vertexCount - 5].position.y,
    cTextGen.verts[(cTextGen.vertexCount - 3)].position.x -
    cTextGen.verts[cTextGen.lines[lId + 1].startCharIdx * 4].position.x,
    cTextGen.lines[lId + 1].height));
    else
    areaForClass[indexes.Key].Add(new Rect(
    cTextGen.verts[cTextGen.lines[lId + 1].startCharIdx * 4].position.x,
    cTextGen.verts[charsIndex[1] * 4 + 3].position.y,
    cTextGen.verts[charsIndex[1] * 4 + 1].position.x - cTextGen
    .verts[cTextGen.lines[lId + 1].startCharIdx * 4].position.x,
    cTextGen.lines[lId + 1].height));
    }
    }

    //simple case - inline link
    }
    else
    {
    //check if ugui cut last vertices due to fact that text is too long for container
    if (charsIndex[1] * 4 + 3 > cTextGen.vertexCount - 1)
    areaForClass[indexes.Key].Add(new Rect(cTextGen.verts[charsIndex[0] * 4].position.x,
    cTextGen.verts[charsIndex[0] * 4 + 3].position.y,
    cTextGen.verts[cTextGen.vertexCount - 3].position.x -
    cTextGen.verts[charsIndex[0] * 4].position.x,
    cTextGen.verts[charsIndex[0] * 4].position.y -
    cTextGen.verts[charsIndex[0] * 4 + 3].position.y));
    else
    areaForClass[indexes.Key].Add(new Rect(cTextGen.verts[charsIndex[0] * 4].position.x,
    cTextGen.verts[charsIndex[0] * 4 + 3].position.y,
    cTextGen.verts[charsIndex[1] * 4 + 1].position.x -
    cTextGen.verts[charsIndex[0] * 4].position.x,
    cTextGen.verts[charsIndex[0] * 4].position.y -
    cTextGen.verts[charsIndex[0] * 4 + 3].position.y));
    }

    i++;
    }
    }
    }
     

Share This Page

  • About Indie Gamer

    When the original Dexterity Forums closed in 2004, Indie Gamer was born and a diverse community has grown out of a passion for creating great games. Here you will find over 10 years of in-depth discussion on game design, the business of game development, and marketing/sales. Indie Gamer also provides a friendly place to meet up with other Developers, Artists, Composers and Writers.
  • Buy us a beer!

    Indie Gamer is delicately held together by a single poor bastard who thankfully gets help from various community volunteers. If you frequent this site or have found value in something you've learned here, help keep the site running by donating a few dollars (for beer of course)!

    Sure, I'll Buy You a Beer