Text Mesh Pro Continuous Scroll Question

I managed to create a much simpler scroller for my purposes since it was taking up the entire bottom part of the screen(I’m building a draft ticker), and it scrolls perfectly fine. Since I can use inline graphics and rich text I can just keep appending picks to the end of the string and only use one Text Mesh pro object.

Now the only question becomes, how can I detect where the end of the Text Mesh Pro object is when it scrolls off the screen to the left so I can restart it again on the right?

I can’t really detect the time because as more draft picks come in, the text box will be getting longer and longer, I would need to actually detect when the object itself leaves the bounds of the canvas on the left side of the screen(I’m assuming position 0 on the X axis). Is it possible to get the position of the right most point of the Text Mesh Pro text box and detect when its position becomes 0 on the X Axis?

Assuming the text is on a single line, you could use the TMP_Text.preferredWidth or TMP_Text.textBounds and if those even use any of the information contained in the TMP_Text.textInfo and sub structures.

In case you are not familiar with the textInfo class, once a text object has been processed (generated) this class contains information about the text object like number of characters, spaces, words, lines, links, etc. It also contains several sub structures like characterInfo[ ], wordInfo[ ], lineInfo[ ], etc. which contain information about the characters, words, lines, etc.

To help you visualize this information, there is a utility script called TMP_TextInfoDebugTool.cs which you can add to any TMP object to show the characters, words, lines, etc.

By using the extend of the text bounds, making sure you factor in the transform position, you can calculate the position of the left or right side of the text object so you can check where either of those are at any given point. Again, if you add that TMP_TextInfoDebugTool.cs script to your text object, you can show the text bounds and also look at how those are handled to draw the bounds.

Between the text bounds, mesh bounds, the textInfo and sub structures, you have everything you need to handle this easily. The text bounds should be sufficient for your needs.

P.S. Whenever properties of the text object are changed, the text object is processed / re-generated late in the update cycle and just before the frame is rendered. This means that if you were to change the text in Awake or Start and then check the textInfo or text bounds, these would not be correct given the text object hasn’t been processed yet. When you change a property of a text object and need to have it processed right away because for instance you want to check the bounds, you can use TMP_Text.ForceMeshUpdate() to force the object to be processed right away. This would result in those bounds and textInfo getting populated with valid data.

1 Like

Thanks for the help Stephan…

I see that the Infintie Scroll in the Text Mesh Pro example pretty much does what I want it to do…however when I made the corrections to it using the ON_TEXT_CHANGED event handlers, Unity crashes when I attempt to run it…

Upon further inspection, the code that Instantiates a clone continuously runs to the point have hundreds of clones before it finally crashes… copied the Awake function from the example here…it continuously runs the first 3 lines of code highlighted in red and never gets beyond that. When it clones the object in line 3, it goes back to the first line of code and starts over running in a continual loop there til it crashes. Any help on why this is happening when it doesn’t happen on the example?

UPDATE: Upon Further inspection, even though m_cloneTextObj is being set to an Instantiated TextMeshProComponent, it still remains null. It then goes back to the beginning of the Awke script, and when it calls the GetComponent<TMP_Text>, it does so on the last cloned object, even though m_cloneTextObj is actually null, it’s somehow Cloning it but not setting it equal to m_cloneTextObj.

private void Awake()
{
m_TextComponent = GetComponent<TMP_Text>();
m_textRectTransform = TextMeshProComponent.GetComponent();
m_cloneTextObj = Instantiate(TextMeshProComponent) as TextMeshProUGUI;
RectTransform cloneRectTransform = m_cloneTextObj.GetComponent();
cloneRectTransform.SetParent(m_textRectTransform);
cloneRectTransform.anchorMin = new Vector2(1, 0.5f);
cloneRectTransform.localScale = new Vector3(1, 1, 1);
}

You need to instantiate new objects to which you add different components.

You could instantiate a new object from a prefab which includes a TMP UGUI component or you could create a new object to which you add the TMP component.

I’m already doing that—I just posted the offending code…here is the complete code:

public class ScrollingTextMgr : MonoBehaviour {
public TextMeshProUGUI TextMeshProComponent;
public float ScrollSpeed = 1;
private TextMeshProUGUI m_cloneTextObj;
private TMP_Text m_TextComponent;
private RectTransform m_textRectTransform;
private bool hasTextChanged;
private void Awake()
{
m_TextComponent = GetComponent<TMP_Text>();
m_textRectTransform = TextMeshProComponent.GetComponent();
m_cloneTextObj = Instantiate(TextMeshProComponent) as TextMeshProUGUI;
RectTransform cloneRectTransform = m_cloneTextObj.GetComponent();
cloneRectTransform.SetParent(m_textRectTransform);
cloneRectTransform.anchorMin = new Vector2(1, 0.5f);
cloneRectTransform.localScale = new Vector3(1, 1, 1);
}
void OnEnable()
{
// Subscribe to event fired when text object has been regenerated.
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
}
void OnDisable()
{
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
}
//checks to see if it the text has changed
void ON_TEXT_CHANGED(Object obj)
{
if (obj == m_TextComponent)
hasTextChanged = true;
}
// Use this for initialization
IEnumerator Start () {
float width = TextMeshProComponent.preferredWidth;
Vector3 startPosition = m_textRectTransform.position;
float scrollPosition = 0;
while (true)
{
//recompute the width of the REctTransfrom if the text object has changed
if (hasTextChanged)
{
width = TextMeshProComponent.preferredWidth;
m_cloneTextObj.text = TextMeshProComponent.text;
}
//Scroll the text across the screen by moving the RectTransform
m_textRectTransform.position = new Vector3(-scrollPosition % width, startPosition.y, startPosition.z);
scrollPosition += ScrollSpeed * Time.deltaTime;
yield return null;
}
}
}

Hmm…I got it working by declaring another public TextMeshProUGUI and setting that up as the child inside the TestMeshProComponent, so it didn’t have to call Instantiate, which is what it didn’t seem to like…

However, unlike in the video, my text only runs one time…the parent text doesn’t pop up when it hits the edge of the screen to start over, it just runs straight on through…

Found out I missed a part early on where he attached the script to an empty gameobject instead of the TextMeshProComponent…that fixed the endless Cloning, so that actually works properly.

However…something seems a little off because:

//Scroll the text across the screen by moving the RectTransform
m_textRectTransform.position = new Vector3(-scrollPosition % width, startPosition.y, startPosition.z);

doesn’t work properly in Unity 2017…the -scrollPosition % width value keeps increasing negatively instead of getting closer to 0 where it should reset itself.

I have it working properly with the following check added in to reset the scroll position when the cloned textbox hits position 0 on the x axis:

if (m_cloneTextObj.rectTransform.position.x <= 0) scrollPosition = 0;

1 Like