Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

TextMesh Pro Clickable link within a text

Discussion in 'UGUI & TextMesh Pro' started by Necronomicron, Jun 11, 2020.

  1. Necronomicron

    Necronomicron

    Joined:
    Mar 4, 2015
    Posts:
    108
    Hello!
    I'm trying to make something like this:
    The docs explain literally nothing.
    This just doesn't work:
    Click <link="http://example.com/">this</link>to be happy.

    And I have no clue what are those IDs and where I have to put them.
     
  2. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    Take a look at example "12 - Link Example" and "12a - Text Interactions" included in the TMP Examples & Extras and the scripts these examples use.

    The link tag format is <link="ID">visible text</link>.

    This ID should be unique to allow your code to implement whatever logic is desired when interacting with this link which in end simply define a region of text.

    Since the desired interaction can be anything. Ie. maybe you want something to happen on mouse over or maybe on click and the result of this interaction is to play a sound or open a window or change the color of the text or whatever else, the <link="ID">link description</link> is generic as to allow you to implement any type of logic.

    Again the link simply allows you to define a section of text and to tag it with a unique ID.
     
    liby99 and Necronomicron like this.
  3. Necronomicron

    Necronomicron

    Joined:
    Mar 4, 2015
    Posts:
    108
    Does something have to happen when I click on the link in 12a? Because nothing happens except some messages in the console.
     
  4. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    In 12a, take a look at the console output as you hover over characters, words, lines, sprites, etc.

    Note: A message is output only when hover over new / different elements.
     
  5. Necronomicron

    Necronomicron

    Joined:
    Mar 4, 2015
    Posts:
    108
    Too much extra work for such a simple task if you ask me. Besides nothing works for me.
    OnPointerClick()
    in
    TMP_TextSelector_B
    seems to be deprecated.
    charIndex
    is always -1. Same for
    linkIndex
    .
     
    Last edited: Jun 11, 2020
  6. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    Make sure you are using the latest release of the TMP package which is Preview 14.

    As per the following summary comment
    <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null if using ScreenSpace Overlay.</param>

    In terms of too much extra work for such a simple task. I agree that it is a complex task which on the surface would appear like a simple task but you have to consider all the potential interaction a user might want.
    - What should we do when someone clicks a link or hover? Play a sound? Instantiate an object? Open a bowser? Does the platform you target support a bowser? Do we open a window? How big... There is no way to know what your expected behavior and requirements are when interacting with a link.

    As such, the <link=ID> simply allows you to define a region of text for which you can implement any potential handling based on your needs.

    Example 12a does show a simpler way to implement the behaviors you might seek using Events.
     
    Necronomicron likes this.
  7. Necronomicron

    Necronomicron

    Joined:
    Mar 4, 2015
    Posts:
    108
    I've already figured out it was because camera parameter isn't null, but where is it from? I don't have this description in IDE.
     
  8. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    The IDE / Intellisense should bring that up for the Camera parameter of the function.

    P.S. I certainly acknowledge the documentation on TMP is horrible and we have folks working it.

    In the mean time, always do a quick search on the forum as there have been many posts about most topics. If your search fails to produce the result you seek, always feel free to post on the forum where I'll be more than happy to provide assistance if other experience TMP users haven't already done so.
     
  9. Necronomicron

    Necronomicron

    Joined:
    Mar 4, 2015
    Posts:
    108
    Well, it didn't. At least for
    FindIntersectingLink()
    . I have the latest TMPro and I use Visual Studio.
     
    Last edited: Jun 11, 2020
  10. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,217
    I don't understand how this is supposed to work. I just want to know when the user clicks certain words in a TMP canvas text field so I can pop up an image.

    In Example 12a, there are two TMP fields, one uses a mesh renderer, and the other uses a canvas renderer. Only the mesh renderer TMP text shows the console output, the canvas renderer TMP field does nothing, as far as I can tell. I don't see why one works and one doesn't, and I'm not sure how the one that works outputs anything to the console. The TMP field in the example has a script attached, TMP_TextEventHandler, but there are no scripts set for any of the selection events. The output comes from another script, TMP_TextEventCheck. Are both these scripts necessary? I've also noticed that these are NOT selection event as the output says, just rollovers. Nothing different seems to happen when a link is clicked.

    How do I detect a click on a link in a TMP canvas text field so that I can call a function when it's clicked? Is there any sample code that works and show how this is done?
     
  11. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    The Event Processor object in that scene has a reference to the text component that uses the mesh renderer. If you replace that reference by other text component (the one using the Canvas Renderer) then it will work and output in the console.

    The TMP_TextEventHandler.cs contains the logic related to what happens when the mouse intersects with the RectTransform of the text object and then with characters, sprites, words, lines, and links. This logic is contained in the LateUpdate function.

    If you look specifically at the "Example of Character or Sprite Selection", you will see the it uses the
    TMP_TextUtilities.FindIntersectingCharacter(). There are similar functions for words, etc.

    In this example, it is only concerned with the mouse intersecting with a character so you would need to add the part about mouse intersecting + clicking as an additional check.

    You would mirror the implementation of "Example of Link Handling" in LateUpdate() but add the additional check for clicking in addition to existing mouse position check.

    You could revise the conditional check as follows:
    Code (csharp):
    1.  
    2. // Handle new Link selection.
    3. if (linkIndex != -1 && Input.GetKeyDown(KeyCode.Mouse0))
    4. {
    5.     m_selectedLink = linkIndex;
    6.  
    7.     // Get information about the link.
    8.     TMP_LinkInfo linkInfo = m_TextComponent.textInfo.linkInfo[linkIndex];
    9.  
    10.     // Send the event to any listeners.
    11.     SendOnLinkSelection(linkInfo.GetLinkID(), linkInfo.GetLinkText(), linkIndex);
    12. }
    13.  
     
  12. John-B

    John-B

    Joined:
    Nov 14, 2009
    Posts:
    1,217
    I attached two scripts to my TMP text field: TMP_TextEventCheck and TMP_TextEventHandler, and set the correct reference. Now, when I move the mouse over the field, a few words, lines, and characters generate console output, apparently at random, but only a few, and it seems to depend on how the field is scrolled. Whatever text happens to be about a 1/3 of the way down the field at the time, as I scroll, different lines/words get flagged. And the ONE LINK I have in the text does nothing, no output when the mouse goes over the link.

    I could save a lot of my time by putting a transparent button over the link, as I've done in the past, and call it a day. But I'd really like to do it the right way and get this to work, and save the hassle of moving the button every time the text changes.
     
  13. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    In order to avoid spamming the console when the mouse position is over a character, word, line, link, etc. A check is made to only handle newer interactions.

    In this portion of code from the TMP_TextEventHandler.cs, you can see that it will only report new characters that it encounters. Removing the charIndex != m_lastCharIndex would result in constent events being raised while the mouse is over a character. Obviously, you can modify / fit this logic to your needs.

    Code (csharp):
    1.  
    2. // Example of Character or Sprite Selection
    3. int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextComponent, Input.mousePosition, m_Camera, true);
    4. if (charIndex != -1 && charIndex != m_lastCharIndex)
    5. {
    6.     m_lastCharIndex = charIndex;
    7.  
    8.     TMP_TextElementType elementType = m_TextComponent.textInfo.characterInfo[charIndex].elementType;
    9.  
    10.     // Send event to any event listeners depending on whether it is a character or sprite.
    11.     if (elementType == TMP_TextElementType.Character)
    12.         SendOnCharacterSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex);
    13.     else if (elementType == TMP_TextElementType.Sprite)
    14.         SendOnSpriteSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex);
    15. }
    16.  
    I would need a better understanding of how your have this setup. Is the text object in some scroll view or something?

    I would suggest first experimenting with a non scrolling text object to make sure everything works as expected. Then add the scrolling part to figure out why that might be an issue.

    If you can't figure it out, please consider providing me with some simple scene / project for me to look at which would enable me to (1) make sure everything is working as expected on the TMP side and then provider pointers as to why you are observing reported behavior.
     
    sohaib_qadri likes this.
  14. mrm83

    mrm83

    Joined:
    Nov 29, 2014
    Posts:
    327
    Did this change between Unity 2019 and 2020? My links are busted in Unity 2020 but works in 2019. Link index returning -1. Tried upgrading to latest version of TMP and the same thing happens.
     
    Last edited: Jun 10, 2021
  15. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    Nothing changed on the TMP front.

    Did you switch to the new input system or to SRP? Looking for perhaps a change to some other system.

    See if the example scenes like 12 and 12a still work as expected on your end.
     
  16. mrm83

    mrm83

    Joined:
    Nov 29, 2014
    Posts:
    327
    Got it. was camera issue.
     
  17. d2clon

    d2clon

    Joined:
    Aug 19, 2017
    Posts:
    19
    This generic script worked for me:

    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using UnityEngine.EventSystems;
    4.  
    5. [RequireComponent(typeof(TMP_Text))]
    6. public class LinkOpener : MonoBehaviour, IPointerClickHandler {
    7.  
    8.     public void OnPointerClick(PointerEventData eventData) {
    9.         TMP_Text pTextMeshPro = GetComponent<TMP_Text>();
    10.         int linkIndex = TMP_TextUtilities.FindIntersectingLink(pTextMeshPro, eventData.position, null);  // If you are not in a Canvas using Screen Overlay, put your camera instead of null
    11.         if (linkIndex != -1) { // was a link clicked?
    12.             TMP_LinkInfo linkInfo = pTextMeshPro.textInfo.linkInfo[linkIndex];
    13.             Application.OpenURL(linkInfo.GetLinkID());
    14.         }
    15.     }
    16.  
    17. }
    18.  
    Source: https://www.feelouttheform.net/unity3d-links-textmeshpro/
     
  18. Gulliver

    Gulliver

    Joined:
    Jan 8, 2013
    Posts:
    93
    doesn't work
     
    S0paTa likes this.
  19. JTSenneker

    JTSenneker

    Joined:
    Jan 18, 2019
    Posts:
    2
    This also worked for me, I just had to make sure the TMP_Text was the TMP component that had the link in it. Thank you for this solution!
     
  20. EmpirisoftNathan

    EmpirisoftNathan

    Joined:
    Sep 6, 2016
    Posts:
    11
    The documentation described in this thread no longer exists, and the old text mesh pro package that contains the scenes is deprecated and no longer usable.

    Where does one learn how to use TextMeshPro in 2021?
     
    sp-LeventeLajtai likes this.
  21. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,581
    The latest TMP package for Unity 2020 or newer is version 3.2.0-pre.1.

    Since preview package discoverability has changed between Unity 2020, 2021 and 2022, you may have to manually reference this new package in the "ProjectRoot/Packages/manifest.json" file.

    The examples for the <link> tag and text interactions are still part of the TMP package. These examples are contained in the TMP Examples & Extras which can be manually imported in the project via the "Window - TextMeshPro - Import TMP Examples & Extras" menu option.

    The examples you should be looking at are 12 - Link Example and 12a - Text Interactions.
     
  22. fafase

    fafase

    Joined:
    Jul 3, 2012
    Posts:
    160
    I think there is a general misunderstanding that link would mean hyperlink that opens a browser.
    Link could be interpreted as meta data for the section of text, at least that how I understand it.

    But hyperlink content can be done with:

    <link="https://www.google.com">Link to Google.com</link>

    It seems that extra space in the tag will break, so no <link = "www.google.com">

    Using the script from previous posts:

    Code (CSharp):
    1.     [RequireComponent(typeof(TMP_Text))]
    2.     public class OpenHyperlink : MonoBehaviour, IPointerClickHandler
    3.     {
    4.         private TMP_Text m_textMeshPro;
    5.         void Start()
    6.         {
    7.             m_textMeshPro = GetComponent<TMP_Text>();
    8.         }
    9.  
    10.         public void OnPointerClick(PointerEventData eventData)
    11.         {
    12.             int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_textMeshPro, Input.mousePosition, null);
    13.             if (linkIndex != -1)
    14.             {
    15.                 TMP_LinkInfo linkInfo = m_textMeshPro.textInfo.linkInfo[linkIndex];  
    16.                 Application.OpenURL(linkInfo.GetLinkID());
    17.             }
    18.         }
    19.     }
    It's the same as above with slight improvement that it finds the component on start. If you have multiple cameras in your scene, this can fail so for instance, I needed extra code:

    Code (CSharp):
    1.     [RequireComponent(typeof(TMP_Text))]
    2.     public class OpenHyperlink : MonoBehaviour, IPointerClickHandler
    3.     {
    4.         private TMP_Text m_textMeshPro;
    5.         private Camera m_uiCamera;
    6.         void Start()
    7.         {
    8.             m_textMeshPro = GetComponent<TMP_Text>();
    9.             Camera[] cameras = FindObjectsOfType<Camera>();
    10.             for(int i = 0; i< cameras.Length; i++)
    11.             {
    12.                 if (cameras[i].CompareTag("UICamera")) // this may be whatever for your case
    13.                 {
    14.                     m_uiCamera = cameras[i];
    15.                     break;
    16.                 }
    17.             }
    18.         }
    19.  
    20.         public void OnPointerClick(PointerEventData eventData)
    21.         {
    22.            // Same
    23.         }
    24.     }
     
  23. ashtorak

    ashtorak

    Joined:
    Feb 19, 2014
    Posts:
    47
    Thanks. What would we do without the forum :D
    My adaptation for new input system including using statements:

    Code (CSharp):
    1. using UnityEngine;
    2. using TMPro;
    3. using UnityEngine.EventSystems;
    4. using UnityEngine.InputSystem;
    5.  
    6.  [RequireComponent(typeof(TMP_Text))]
    7.     public class OpenHyperlink : MonoBehaviour, IPointerClickHandler
    8.     {
    9.         private TMP_Text m_textMeshPro;
    10.         void Start()
    11.         {
    12.             m_textMeshPro = GetComponent<TMP_Text>();
    13.         }
    14.         public void OnPointerClick(PointerEventData eventData)
    15.         {
    16.  
    17.             int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_textMeshPro, new Vector3(Mouse.current.position.x.ReadValue(), Mouse.current.position.y.ReadValue(), 0), PROVIDEYOURCAM);
    18.             if (linkIndex != -1)
    19.             {
    20.                 TMP_LinkInfo linkInfo = m_textMeshPro.textInfo.linkInfo[linkIndex];
    21.                 Application.OpenURL(linkInfo.GetLinkID());
    22.             }
    23.         }
     
  24. masterton

    masterton

    Joined:
    Dec 11, 2012
    Posts:
    31
    If you can't get this to work - your canvas might be "Screen Space - Overlay" - in which case you should pass null as the camera component.

    An older post from Stephan B
     
  25. Nightmare34

    Nightmare34

    Joined:
    Oct 3, 2020
    Posts:
    3
    You can also use :
    eventData.position.x, eventData.position.y instead of Mouse.current.position.x.ReadValue(), Mouse.current.position.y.ReadValue(). This way your are not dependent of the New or old input system.
     
    ashtorak likes this.
  26. Kilik1985

    Kilik1985

    Joined:
    Oct 10, 2022
    Posts:
    2
    I pass null for the camera and eventData.position but the app does nothing when I click on a link. The code works when linkIndex != -1 but it's like the script stops working when I do click on a link.

    The variable linkIndex doesn't even get a value when I click on the link. I set a breakpoint on the line and I can't get any value, it's as if the code just doesn't execute anymore.

    EDIT: nothing to do with the script, sorry. My link was at the bottom of a viewport and clicks aren't being registered there. That's another issue I have to figure it out now
     
    Last edited: Dec 2, 2022
  27. FilippoG

    FilippoG

    Joined:
    Nov 18, 2019
    Posts:
    15
    There is no way I can get links to work...
    Camera is found, mesh too... Still nothing happens when clicking on link.
    OnPointerClick is never triggered.
    I add the script to the Text component in the canvas, but nothing.
     
  28. FilippoG

    FilippoG

    Joined:
    Nov 18, 2019
    Posts:
    15
    Git it. Input events are not registered on dinamically sized TextMesh. It is only registered within the initial dimensions of mesh set in editor.
    If Text is resized by text content, nothing is registered on newly occupied space (no rollover, rollout or clicks, nothing).
    This is ridiculous.
    We could handle this sort of things in Flash in a breeze at the end of the '90s...