Draggable Chracters in TextMesh Pro

I want to make an anagram puzzle solver; so the behaviours I’m going to need in my script are ‘muddling’ the characters in a word and allowing the player to drag and drop the characters in a word to form new sequences.

I have written a script that implements the IBeginDragHandler, IDragHandler, IEndDragHandler interfaces to drag RectTransforms around on a canvas, and I was originally going to have a TextMesh Pro object for each letter. But I have since learned that TextMesh Pro contains a lot of definitions for word and text structure, and I want to leverage that.

However, I have been unable to find a way to move a character mesh according to mouse position. I have done a lot of research in the past week, and all I have been able to accomplish is this mess:

using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[RequireComponent(typeof(TextMeshProUGUI))]
public class Test : MonoBehaviour, IInitializePotentialDragHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private TMP_Text text;
    private TMP_TextInfo textInfo;
    private TMP_CharacterInfo charInfo;
    private int charIndex;
    private int vertexIndex;
    private Vector3[] vertices;
    private Matrix4x4 matrix;                   //?
    private RectTransform canvasRectTransform;  // The RectTransform of the Canvas.
    private RectTransform rectTransform;        // The RectTransform of the Draggable GameObject.
    private Vector2 offset;                     // The mouse position offset on the Draggable GameObject.
    private Vector3 origin;                     // The origin position of the Draggable GameObject.

    private Vector3 bottomLeft;
    private Vector3 topLeft;
    private Vector3 topRight;
    private Vector3 bottomRight;
    private RectTransform rt;
    private Rect rect;

    private TextMeshPro textMeshPro;

    private void Awake()
    {
        text = GetComponent<TMP_Text>();
        textMeshPro = GetComponent<TextMeshPro>();

    }

    public virtual void OnInitializePotentialDrag(PointerEventData eventData)
    {
        rectTransform = eventData.pointerEnter.transform as RectTransform;
        canvasRectTransform = eventData.pointerEnter.transform.GetComponentInParent<Canvas>().transform as RectTransform;
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        // Generate the mesh and populate the textInfo with data we can use and manipulate.
        text.ForceMeshUpdate();

        charIndex = TMP_TextUtilities.FindIntersectingCharacter(text, Input.mousePosition, eventData.pressEventCamera, true);
        textInfo = text.textInfo;
        charInfo = textInfo.characterInfo[charIndex];
        vertices = textInfo.meshInfo[0].vertices;

        bottomLeft = canvasRectTransform.InverseTransformPoint(charInfo.bottomLeft);
        topLeft = canvasRectTransform.InverseTransformPoint(charInfo.topLeft);
        topRight = canvasRectTransform.InverseTransformPoint(charInfo.topRight);
        bottomRight = canvasRectTransform.InverseTransformPoint(charInfo.bottomRight);

        rect = new Rect(topLeft.x, topLeft.y, Mathf.Abs(topLeft.x - topRight.x), Mathf.Abs(topLeft.y - bottomLeft.y));

        text.havePropertiesChanged = true;

        //textMeshPro.renderMode = TextRenderFlags.DontRender; // Instructing TextMesh Pro not to upload the mesh as we will be modifying it

        vertexIndex = textInfo.characterInfo[charIndex].vertexIndex;

        Debug.Log(charInfo.character);
    }

    public void OnDrag(PointerEventData eventData)
    {
        // Compute the baseline mid point for each character
        //Vector3 offsetToMidBaseline = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[charIndex].baseLine);

        //// Apply offset to adjust our pivot point.
        //vertices[vertexIndex + 0] += -offsetToMidBaseline;
        //vertices[vertexIndex + 1] += -offsetToMidBaseline;
        //vertices[vertexIndex + 2] += -offsetToMidBaseline;
        //vertices[vertexIndex + 3] += -offsetToMidBaseline;

        //var vertex0Screen = RectTransformUtility.WorldToScreenPoint(eventData.pressEventCamera, vertices[vertexIndex + 0]);
        //var vertex1Screen = RectTransformUtility.WorldToScreenPoint(eventData.pressEventCamera, vertices[vertexIndex + 1]);
        //var vertex2Screen = RectTransformUtility.WorldToScreenPoint(eventData.pressEventCamera, vertices[vertexIndex + 2]);
        //var vertex3Screen = RectTransformUtility.WorldToScreenPoint(eventData.pressEventCamera, vertices[vertexIndex + 3]);


        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, eventData.position, eventData.pressEventCamera, out Vector2 mouseLocal);

        bottomLeft = mouseLocal;

        vertices[vertexIndex + 0] = canvasRectTransform.TransformPoint(bottomLeft);

        //RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, vertex0Screen, eventData.pressEventCamera, out Vector2 vertex0Local);
        //RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, vertex1Screen, eventData.pressEventCamera, out Vector2 vertex1Local);
        //RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, vertex2Screen, eventData.pressEventCamera, out Vector2 vertex2Local);
        //RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, vertex3Screen, eventData.pressEventCamera, out Vector2 vertex3Local);

        //vertices[vertexIndex + 0] = canvasRectTransform.TransformPoint(mouseLocal);

        //TMP_TextUtilities.ScreenPointToWorldPointInRectangle(text.transform, eventData.position, eventData.pressEventCamera, out Vector3 mouseWorld);

        //var vertex0World = canvasRectTransform.TransformPoint(new Vector2(rect.xMin, rect.yMax));
        //var vertex1World = canvasRectTransform.TransformPoint(new Vector2(rect.xMin, rect.yMin));
        //var vertex2World = canvasRectTransform.TransformPoint(new Vector2(rect.yMax, rect.yMin));
        //var vertex3World = canvasRectTransform.TransformPoint(new Vector2(rect.yMax, rect.yMax));

        ////Debug.Log(position.ToString());

        //vertices[vertexIndex + 0] = vertex0World;
        //vertices[vertexIndex + 1] = vertex1World;
        //vertices[vertexIndex + 2] = vertex2World;
        //vertices[vertexIndex + 3] = vertex3World;

        //matrix = Matrix4x4.TRS(mouseWorld, Quaternion.identity, Vector3.one);

        //vertices[vertexIndex + 0] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 0]);
        //vertices[vertexIndex + 1] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 1]);
        //vertices[vertexIndex + 2] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 2]);
        //vertices[vertexIndex + 3] = matrix.MultiplyPoint3x4(vertices[vertexIndex + 3]);

        //vertices[vertexIndex + 0] += offsetToMidBaseline;
        //vertices[vertexIndex + 1] += offsetToMidBaseline;
        //vertices[vertexIndex + 2] += offsetToMidBaseline;
        //vertices[vertexIndex + 3] += offsetToMidBaseline;

        text.mesh.vertices = vertices;
        text.mesh.uv = textInfo.meshInfo[0].uvs0;
        text.mesh.uv2 = textInfo.meshInfo[0].uvs2;
        text.mesh.colors32 = textInfo.meshInfo[0].colors32;

        // Upload the mesh with the revised information
        text.UpdateVertexData();

        //textMeshPro.mesh.RecalculateBounds(); // We need to update the bounds of the text object.

        //text.UpdateVertexData(TMP_VertexDataUpdateFlags.Vertices);

        //text.ForceMeshUpdate();
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        //text.UpdateVertexData(TMP_VertexDataUpdateFlags.Vertices);

        //text.ForceMeshUpdate();
    }
}

Could someone more experienced with TextMesh Pro please point me in the right direction as to what my code should look like inside the IDragHandler method for moving a character mesh according to mouse position?

Just letting you know that I saw your post but currently slammed / crazy busy. Hopefully, we can get some member of the community familiar with TMP to help you out on this. If not and as soon as I have time, I’ll circle back on this.