With TextMeshPro + UGUI an effect like this is achievable easily by using the maxVisibleCharacters property. This lets you do a “typewriter effect” (eg Visual Novels, Final Fantasy text boxes, etc). However, in TextCore it seems like this isn’t exposed. I could keep adding characters to the string, but this would cause words to jump between lines as soon as they got too wide (very awkward and noticeable).
I looked in TextParams and it seems like it’s not there. Before I start poking around more with reflector…
Is this exposed somehow already?
If not, is this on the roadmap as a planned feature for the near future?
If not, is there a relatively simple way to do this? Perhaps override Label’s mesh generation and then only take a subset of the vertices?
If not, is there a straightforward way to bridge TextMeshPro into UIToolkit without invoking UGUI?
If not, is there a sensible way to integrate UGUI that won’t break a bunch of scaling and Z ordering?
The logic related to maxVisibleCharacters is already in TextCore, but it isn’t exposed through USS yet. Thanks for bringing it up, I added a task to track this and hopefully have it available for 21.2.
Unfortunately, I don’t see an easy workaround here, but you should be able to mix UGUI, TextMeshPro and UI Toolkit for runtime in the same scene. Just be aware that having a lot of interleaved elements could impact performance.
Mixing them works fine for now, although it’s a shame having 2 of the same font resource. And scaling is going to be an issue, although hopefully by the time I have to worry about that, it’ll be part of TextCore.
I had a little go at this with Yew, because that’s what you do I guess, when you’re trying something new.
public class TypeWriter : View
{
public string Text { get; set; }
class Component : YewLib.Component
{
private TypeWriter Props { get; set; }
public Component(TypeWriter props) => Props = props;
public override View Render()
{
var len = UseState(0);
UseRaf(() =>
{
if (len >= Props.Text.Length) return false;
if (Random.value > 0.3) return true;
len.Value++;
return false; // no need to call another raf because render will do it for us.
});
string text = Props.Text;
if (len < text.Length)
text = $"{text.Substring(0, len)}<alpha=#00>{text.Substring(len)}";
return Label(text, className: "typewriter");
}
}
}
can be used like:
Yew.Render(new TypeWriter { Text = "you walk into a dark room..." }, uiDoc.rootVisualElement);
No, it doesn’t jump around. I’m rendering the whole string each time, but with the ‘unseen’ part as fully transparent text. That’s what the ‘<alpha=#00’ bit is doing on line 23.
I made it partially opaque and slowed it down a bit, you can see here:
I got the idea from the TextMesh Pro guy in this video:
OK. I really need to go to bed. But, this is too much fun.
Here’s a version that uses Unity’s coroutines. This seems to be more like how unity likes to do things maybe? I added a button after the text appears, which feels very ‘game-y’.
and here’s how the animation works:
public override View Render()
{
var len = UseState(0);
var buttonOpacity = UseState(0f);
IEnumerator anim()
{
while (len < Props.Text.Length) {
len.Value++;
yield return new WaitForSeconds(0.1f);
}
while (buttonOpacity < 1) {
buttonOpacity.Value += 0.03f;
yield return new WaitForSeconds(0.025f);
}
}
UseCoroutine(anim);
string text = Props.Text;
if (len < text.Length)
text = $"{text.Substring(0, len)}<alpha=#44>{text.Substring(len)}";
return new StackLayout()
{
Label(text, className: "typewriter"),
new Button("Examine body", () => { }, opacity: buttonOpacity)
};
}
It, unfortunately, hasn’t been exposed yet. You would need to implement your own version using an approach similar to what’s mentioned above. I am sorry for the false hope. I would be glad to give you a hand implementing your own version if need be.
I’m curious if there is a reason the maxVisibleCharacters property is not implemented/exposed?
I’m missing it right now in a UI Toolkit project. A shame this hasn’t been exposed as it is a nice clean solution. Frankly what I wish for just now is to be able to use TextMeshPro in a VisualElement instead of the simple Label VisualElement.
It seems text handling has taken a backwards step with UI Toolkit.
@HugoBD-Unity here’s my vote +1 for maxVisibleCharacters getting exposed in USS, I’m using the above workaround but support would be really helpful for the Yarn Spinner community, thanks.
I don’t really find any of these heavily-allocating workarounds very satisfying, and I can’t seem to make the internal code that actually implements maxVisibleCharacters in TextCore work without it also affecting other text elements, (presumably because the maxVisibleCharacters isn’t being reset in the UITKTextHandle). Very frustrating!
This works fine, however as soon as I want to style individual words in the text to highlight important information to the player, the typewriter effect shows the inline tags as it is writing the text which is obviously not usable.
I genuinely can’t believe after 3 years and “hoping to have it in by 21.2”, that we’ve made it to the Unity 6 beta and it still hasn’t been made available to us. It doesn’t even need to be exposed via USS, if it can just be made settable via C# that would be more than enough as a step 1 towards getting this task done.
I’m sorry for the disappointment. To be 100% transparent, one of our interns started working on the feature, but it was unfortunately not finished in time. The main remaining blocker was support for the USS transition.
It’s probably clear already, but as Kandy_Man said, this feels like it could be first implemented without USS support, which would mean that it’s even possible to implement your own.
In future can’t features be broken down to separate the C# and USS implementations? So if stuff like this happens it can be broken down over multiple releases instead of presumably sitting on a branch as a whole with a large usable portion waiting for the USS capability.