- Home /
Get position of specific letter in UI Text
Is there any way to get the position of a specific letter (or any part of the string) in a UI text? In World Space, Screen Space, Canvas Space, whatever.
Say I have text that can be "Level 1" or "Level 1000". It's middle-aligned so the letter L can be anywhere on the screen, but I want to move a GameObject right beside the L. How would I find that?
This is using Unity's UI canvas. Specifically with Screen Space - Overlay, but ideally I'd like a solution that works regardless of screen space/world space type.
Thanks!
I want to achieve the same in my project as well. Please let me know if someone already has an answer for this.
If you just need the left side of the text since the 'L' is the left most character you can use RectTransform.GetWorldCorners for a one liner. for multiple lines you would need the cachedTextGenerator of the Text-Component with GetCharacters or GetCharactersArray. If you know your 'L's index (0 in your case) you'll get the cursor position in local space
Answer by Pratap-Dafedar · Nov 14, 2016 at 02:50 PM
Hi Here is the scriptsnippet which will help you find exact letter position in Unity UI Text.
public class TextTest : MonoBehaviour
{
public Text textComp;
public int charIndex;
public Canvas canvas;
void PrintPos ()
{
string text = textComp.text;
if (charIndex >= text.Length)
return;
TextGenerator textGen = new TextGenerator (text.Length);
Vector2 extents = textComp.gameObject.GetComponent<RectTransform>().rect.size;
textGen.Populate (text, textComp.GetGenerationSettings (extents));
int newLine = text.Substring(0, charIndex).Split('\n').Length - 1;
int whiteSpace = text.Substring(0, charIndex).Split(' ').Length - 1;
int indexOfTextQuad = (charIndex * 4) + (newLine * 4) - 4;
if (indexOfTextQuad < textGen.vertexCount)
{
Vector3 avgPos = (textGen.verts[indexOfTextQuad].position +
textGen.verts[indexOfTextQuad + 1].position +
textGen.verts[indexOfTextQuad + 2].position +
textGen.verts[indexOfTextQuad + 3].position) / 4f;
print (avgPos);
PrintWorldPos (avgPos);
}
else {
Debug.LogError ("Out of text bound");
}
}
void PrintWorldPos (Vector3 testPoint)
{
Vector3 worldPos = textComp.transform.TransformPoint (testPoint);
print (worldPos);
new GameObject ("point").transform.position = worldPos;
Debug.DrawRay (worldPos, Vector3.up, Color.red, 50f);
}
void OnGUI ()
{
if (GUI.Button (new Rect (10, 10, 100, 80), "Test"))
{
PrintPos ();
}
}
}[link text][1]
Note: This logic works pretty well with any font and any text properties. Attached unity package to show same script sample.
Tested in : Unity 5.4.0f3 @Sujil-V-S Try this...
[1]: /storage/temp/82165-findletterposinuitext.zip
why are we recreating TextGenerator, can we not use the Textgenerator present in the Text, like this- textComp.cachedTextGenerator;
How would I make this work with the canvas scaler in the mode "Scale with Screen Size"? I'm assu$$anonymous$$g that it has something to do with textGen.Populate using the wrong location, but I'm not sure because I've never worked with Text Generators before.
Thank you for your code. But I don’t really understand what coordinates I get ... I can’t put either Image or SpriteRenderer in its ... It would be great to add an example using it.
I think there is a small error in the Pratap-Dafedar code. The "whitespace" variable is not being used.
I needed to change this line: int indexOfTextQuad = (charIndex 4) + (newLine 4) - 4;
To: int indexOfTextQuad = (charIndex 4) + (newLine 4) - (whiteSpace * 4);
Answer by hengde · May 26, 2017 at 06:06 PM
@Halleester and anyone else who needs this to work with the canvas set to scale with screen size:
simply divide the position returned for a character by the canvas.scaleFactor
worldPos /= canvas.scaleFactor
and it should work fine. I just got it working myself.
Answer by Jbaker08 · Oct 16, 2018 at 02:35 PM
@Halleester, if you're using this on a canvas with a canvas scaler component, you need to divide it by the scale factor. @hengde's solution won't actually work due to the canvas scaler being broken, canvas.scalefactor will always return 1 since it takes a frame to get the scale information. To fix this store a variable that either gets defined after the scale value has initalized (one frame will do) or set it in an update function.
float screen_scale;
void Update()
{
screen_scale = canvas.scaleFactor;
}
Then I had to divide the scalefactor in the actual returned value.
return PrintWorldPos(avgPos / screen_scale);
For some reason, dividing it by the screen scale didn't work when I added into the 'printworldpos' function.
Thanks
Answer by abdullahahmetaskin · Jun 20, 2021 at 06:42 PM
Guys if you looking for text mesh pro version of this code i have working one . It is like this
public GameObject GetPositionOfLastLetterAsGameObject(TextMeshProUGUI tmp_text)
{
tmp_text.ForceMeshUpdate();
Vector3[] vertices = tmp_text.mesh.vertices;
TMP_CharacterInfo charInfo = tmp_text.textInfo.characterInfo[tmp_text.textInfo.characterCount - 1];
int vertexIndex = charInfo.vertexIndex;
Vector2 charMidTopLine = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, (charInfo.bottomLeft.y + charInfo.topLeft.y) / 2);
Vector3 worldPos = tmp_text.transform.TransformPoint(charMidTopLine);
GameObject charPositionGameObj = new GameObject("PositionOfLastChar");
charPositionGameObj.transform.position = worldPos;
return charPositionGameObj;
}
Your answer
Follow this Question
Related Questions
Changing vertex positions of a uGUI Text Mesh 0 Answers
How to change the Top and Bottom (rect.yMin and yMax) properties of a rectTransform, in a script? 2 Answers
UI Text position not as shown in scene view? 1 Answer
How to change the gameobjects position and transform in a scene when the device orientation changes. 1 Answer