Collider to TextMesh Pro text

I need collider for the text. Is it possible to do somehow?

Are you talking about a collider for each individual letters?

If so, it can actually be done. Please note this was done just for fun and requires some tweaking of source code of TMP.

The first step is to modify the code designed to get over the 65,535 vertices limit. This code, breaks the text object into sub meshes whenever the text would exceed this 65,535 vertices limit. However, in our case, we actually want to force TMP to create a sub mesh per character so we can add a collider to each of them and control them separately.

Depending on the version of TMP that you are using (which needs to be a source code version. Any of the new versions of TMP provided via the package manager for 2018.1 + include source code).

In the TMPro_Private.cs file around line 1272, is where these vertices limit is handled. Change the 16383 to a value of 1. This will force TMP to create a new sub mesh object for each character.

if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B)
{
    // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
    if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
        m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
    else
    {
        m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
        m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
    }
}

Then at line 3820 or thereabout, uncomment the following line which will automatically add / size the colliders on these characters.

// Update the collider on the sub text object
m_subTextObjects[i].UpdateColliders(m_textInfo.meshInfo[i].vertexCount);

Next in the TMP_SubMesh.cs file at around line 184 uncomment the following property and serialized field.

public BoxCollider boxCollider
{
    get
    {
        if (m_boxCollider == null)
        {
            //
            m_boxCollider = GetComponent<BoxCollider>();
            if (m_boxCollider == null)
            {
                m_boxCollider = gameObject.AddComponent<BoxCollider>();
                gameObject.AddComponent<Rigidbody>();
            }
        }
        return m_boxCollider;
    }
}
[SerializeField]
private BoxCollider m_boxCollider;

Lastly, uncomment function at line 558 as follows.

public void UpdateColliders(int vertexCount)
{
    if (this.boxCollider == null) return;
    Vector2 bl = TMP_Math.MAX_16BIT;
    Vector2 tr = TMP_Math.MIN_16BIT;
    // Compute the bounds of the sub text object mesh (excluding the transform position).
    for (int i = 0; i < vertexCount; i++)
    {
        bl.x = Mathf.Min(bl.x, m_mesh.vertices[i].x);
        bl.y = Mathf.Min(bl.y, m_mesh.vertices[i].y);
        tr.x = Mathf.Max(tr.x, m_mesh.vertices[i].x);
        tr.y = Mathf.Max(tr.y, m_mesh.vertices[i].y);
    }
    Vector3 center = (bl + tr) / 2;
    Vector3 size = tr - bl;
    size.z = .1f;
    this.boxCollider.center = center;
    this.boxCollider.size = size;
}

This should do the trick.

Again this was just experimental stuff for fun. Some new property should be added to the text object to control this vertices limit as with this change all text objects will have this 1 character limit which is most likely not want you want in all instances.

1 Like

Hello Stephan, i tried your solution and it work at beginning but when i try to put a lot of characters (more than 32) it show some errors in the console and didnt work :

TMPro.MaterialReference.AddMaterialReference (UnityEngine.Material material, TMPro.TMP_FontAsset fontAsset, TMPro.MaterialReference[ ] materialReferences, System.Collections.Generic.Dictionary`2[TKey,TValue] materialReferenceIndexLookup) (at Packages/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs:596)```

and 

NullReferenceException: Object reference not set to an instance of an object
TMPro.TextMeshPro.GenerateTextMesh () (at Packages/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs:1864)```

can you explain what is the problem and what can i do to solve it.
Thank you.
LoĂŻs

Most likely the array allocations for the sub mesh objects (1 per character) is not being resized when you get above 32.

A quick and dirty solution is to increase the default allocation to 64 in the TMP_Text.cs as follows:

protected MaterialReference[ ] m_materialReferences = new MaterialReference[64];

This is assuming you will be using less than 64 characters.

Note: When I have time, I’ll take a closer look at this implementation to make sure it behaves as expected by default.

Hi. I have a lot of objects with TextMeshUGUI components with only 1 charater in each. Is there any way to create polygon collider with script that will repeat character mesh? The main reason is to detect mouse click at exact letter. As you can see in my project simple box colliders will overlap each other. And IPointerClick wont work properly.

Adding a Collider per character to detect mouse clicks is not necessary.

Please see example “12 - Link Example” and “12a - Text Interactions” which provides examples of potential interactions with characters, words, links, etc. These examples are included in the TMP Examples & Extras. Please review the included scripts to learn how you can use this existing functionality.

I came across these examples and it is good. But click areas are rectanges for each char. So in this example I cant select “X” if it is not “at front” in ui sorting.

5631733--584830--2020-03-26_130834.png

The functions used to find the nearest character is contained in TMP_TextUtilities.cs file, you might be able to add by modifying one of those functions to check if two potential position finds more than one potential character and then add additional logic to determine which should be selected.

Assuming you find some good logic here, this would be more efficient then adding several colliders per character.

Thanks for fast reply. As I can understand it will work for several chars within one object. But as I mentioned i have separeted gameobjects for each char. And raycasting works according to sibling index. Are you sure you lead me to the right way (using FindNearestCharacter())?

There are several functions in the TMP_TextUtilities.cs file which are used in those examples to figure out what character, word, line, etc. intersect with a given position. Take a look at the scripts used in those examples to see how these functions are used.

P.S. Pay close attention to the Camera setting where it may need to be null when in Screen Space Overlay for instance.

Hi Stephan_B :slight_smile:

I would like to add colliders so a player can walk on the text as in the manually manipulated polygon collider below.
The polygon collider defaults to a shape that does not match the letter.
Should I look into the TMP_TextSelector_B.cs as a means to do so? Is this a good direction?
Otherwise, I suppose I would only need to do a few more than 52 characters manually in order to get the entire
alphabet in upper and lower case.
Any advice?
Thanks!

I couldn’t find out TMPro_Private.cs file. How do this in 2022.3.9f1.