Is textBounds cached anywhere?

I was just reading the code for:

        /// <summary>
        /// Returns the bounds of the text of the text object.
        /// </summary>
        public Bounds textBounds
        {
            get
            {
                if (m_textInfo == null) return new Bounds();

                return GetTextBounds();
            }
        }

Is this cached anywhere? Seems expensive to call every frame within the context of typewriting.

It is not cached but it only iterates over the existing / cached textInfo content.

Having said that, why do you need to call it every frame?

I understand that you have some type of type writing implementation. Just curious of how you have this implemented and if you are using something similar to the 17 - Old Computer Terminal example included in the TMP Examples & Extras?

Ah so the typewriter stuff is happening inside of a scroll view. If I do what that typewriter example does, then the second I add in text, the scroll view will stretch to fit the text regardless of maxVisibleCharacters.

I made a video here so you can see what I mean. The first half of the video shows what I want to have happen, the second half shows what actually happens.

Here’s that video.

Are you using Layout Components / Content Size Fitter to stretch to fit the content object?

Is your Typewriter implementation using MaxVisibleCharacter where you get the bounds per frame and then update the content object based on those bounds?

Yep, I’m using Content Size Fitter and Layout Components to make the content box stretch with the content.

The typewriter implementation does use MaxVisibleCharacters and it’s set to reveal a certain number of characters over time.

The text component has a LayoutElement on it and in the first example in that video where the box expands as the text fills up, the script is updating the PreferredHeight based on the bounds.y for the text.

If you’d like, I can try to throw together some code if that’d be helpful.

Just wanna bump this because it seemed like you had more to say.

If you can provide me with some repro project / scene, I would be more than happy to take a closer look.

Awesome! Here’s a small project that reproduces the code I’m talking about.

If you hit play, it should do the typewriter with the behavior I’m hoping for. The relevant code is in VoodooMinigameTypewriter. If you disable the “auto scroll” option, it’ll stop doing that expensive code bounds checking, but it’ll also stop working correctly.

Hey just wanted to bump this

Will try to look into this over the weekend and follow once I have more information.

No worries, please let me know if you have any questions! I really appreciate the help!

Hey just wanted to check in and see if you had a moment to check out the example scene?

Just wanted to bump this again

Sorry about the delay in follow up. Simply buried in work. Will look at it tonight.

Finally had a chance to review your implementation. The following revision will be more efficient as the text object will only get updated once per iteration.

using PixelCrushers.DialogueSystem;
using UnityEngine;

namespace Dialogue
{
    public class VoodooMinigameTypewriter : TextMeshProTypewriterEffect
    {
        private Bounds cachedBounds;
        private int lastLineCount;

        protected override void HandleAutoScroll()
        {
            if (!autoScrollSettings.autoScrollEnabled) return;

            // Make sure we have a valid range of typed characters.
            if (charactersTyped == 0 || charactersTyped - 1 > textComponent.textInfo.characterCount)
                return;

            // Determine the line number of the current character.
            int currentLine = textComponent.textInfo.characterInfo[charactersTyped - 1].lineNumber;

            // Compute the height of the text
            // This corresponds to the first line Ascender to current line Descender.
            float textAscender = textComponent.textInfo.lineInfo[0].ascender;
            float textDescender = textComponent.textInfo.lineInfo[currentLine].descender;
            float textHeight = textAscender - textDescender + textComponent.margin.y + textComponent.margin.z;

            // Set Height
            layoutElement.preferredHeight = textHeight;

            // Scroll
            if (autoScrollSettings.scrollRect != null)
            {
                autoScrollSettings.scrollRect.normalizedPosition = Vector2.zero;
            }
            if (autoScrollSettings.scrollbarEnabler != null)
            {
                autoScrollSettings.scrollbarEnabler.CheckScrollbar();
            }
        }
    }
}

The change is to essentially remove the ForceMeshUpdate() which forces an extra layout pass on the text object.

The ForceMeshUpdate() was required in your implementation which needed the bounds of the text geometry to be updated in order to set the text height. However, in this revised implementation and since the layout of the whole text and its metrics do not change as characters are revealed, we can use the text metrics like lineInfo to calculate its height.

This could be further optimized but saving on the extra layout passes should account for the largest gains.

This is perfect! Thank you so, so much! I knew there had to be some way to calculate this without regenerating the bounds.

Out of curiosity, you said this could be further optimized. How would I do that?

First, I am not sure if the gains would be significant enough to warrant further optimizations but if we really wanted to further optimize this, you could look to only change the height on line changes as opposed to per character. So basically, in your code where you call HandleAutoScroll(), only call it when a new character ends up on a new line which is the only time that you need to update the preferredHeight.

Additional optimizations that could provide larger gains would be more involved and require modifying the vertex color alpha of the geometry instead of using maxVisibleCharacters which does cause a full layout pass.

This implementation would resemble the VertexJitter.cs where you would be iterating over the geometry of the text to control the alpha of individual characters. Currently when we use .maxVisibleCharacters, we get a full layout pass per update of this property. Assuming the text container is static from a text layout point of view, we would only need to do a full layout pass once. Then via a new script control the alpha of each character to do the reveal. So regardless of the number of characters, we would only have one full layout pass. You would still need to update the layoutElement.preferredHeight when the newly revealed character is on a new line.

Ahhhh I see, OK thanks very much for all the details! I think unless we have trouble with this implementation, I won’t shoot for those additional optimizations for now. But that’s super helpful to know about in case there’s any trouble down the road. Thank you very much!