UGTK cat mascotUGTKengine within an engine Request access

UGTK / Toolkit / Ui Module / Text Typer System Completed

Ui Module — UGTK

Text Typer System

Summary

The Text Typer System reveals a TextMeshPro text one character at a time — the classic typewriter. A single component, Mn_TextTyperComponent, does everything: forward typing, two kinds of reverse typing (from the end and from the start), an optional per-character fade, a large UnityEvent-friendly API, and a custom inspector with buttons to drive it all without a line of glue code.

Note: the system is kept for backward compatibility and keeps working exactly as always. For new work the richer typewriter lives in TextAnimatorUGTK; this component can delegate to it by turning on Use Text Animator (see TextAnimatorUGTK integration), keeping every existing reference and inspector event untouched.


Content


Modules Dependencies

Only in the demo scene. The Debug scene of this module also uses GameplaySystems/AudioSystem. Nothing in the runtime code needs it: import this module on its own and it works, but the demo scene opens with a missing script where that component was.


Setup

Drop the TextTyper prefab under a Canvas — a TextMeshProUGUI with Mn_TextTyperComponent already wired to itself (0.2 s per character, 0.2 s fade, Play On Start on, a Lorem ipsum message):

There is a second prefab, TextTyperBox: the same typer inside a ScrollRect with a background and a vertical scrollbar — the ready-made dialogue box.

Or set it up by hand: add Mn_TextTyperComponent to any GameObject and assign Target Text Mesh.


How To Use

Tune the timing under Writing Settings — speed up the typing and shorten the fade with it:

Further down, the inspector's Timing section recomputes live how long a character step really lasts and how much of the fade actually fits inside it — see Inspector Controls.

Core - Target Text Mesh: the TextMeshProUGUI to write into. Without it the component logs an error and fires the onFinish… event straight away. - Play On Start: calls StartWrite() in Start(). - Prepare Invisible On Enable: on Awake/OnEnable the TMP is put in the "text assigned, zero visible characters" state. Leave it on — it is what prevents the classic one-frame flash of the whole message before typing begins. - Writing Settings: timePerCharacter, startDelay, fadeDuration (see below). - Message: the text to type (multiline).

The timing rule — this is the part that surprises people: - every character step lasts exactly timePerCharacter seconds (scaled time); - fadeDuration runs inside that step and is clamped to timePerCharacter, so it never makes the typing longer. Want a visible fade? Raise timePerCharacter, don't raise the fade; - with timePerCharacter = 0 the whole message appears in the same frame — and every onCharacterTyped still fires, once per non-whitespace character; - negative values are clamped to 0 on Awake, on OnValidate and on every setter.

textTyper.StartWrite();                     // types `message` forward
textTyper.SetMessage_AndStartWrite("Ciao"); // change + type, in one call

textTyper.StartReverseWrite();              // erases from the END, last char first
textTyper.StartReverseWriteFromStart();     // erases from the START, first char first
textTyper.StopTyping();                     // freeze where it is
textTyper.ResetVisibleCharactersToZero();   // hide everything again, keeping the layout

textTyper.SetInstantTypingOn();             // "skip" button: finishes the current write now
textTyper.SetTimePerCharacter_ApplyNow(0.05f);
bool typing = textTyper.IsTyping();
var state   = textTyper.GetTypingState();   // Idle / WritingForward / WritingReverseFromEnd / WritingReverseFromStart

Settings can be changed while typing: every routine re-reads them at each step. The setters come in three flavours so they can be wired straight to a Button's onClick: SetX(value) (applies to the next write), SetX_ApplyNow(value) (applies to the write in progress) and SetX_Restart(value) (restarts the current routine with the new value).

Events — three families, one per direction: onStartWriting / onCharacterTyped / onFinishWriting, onStartReverseWriting / onCharacterReverseTyped / onFinishReverseWriting, onStartReverseFromStartWriting / onCharacterReverseFromStartTyped / onFinishReverseFromStartWriting. The per-character events fire only for non-whitespace characters, so a typing sound does not click on spaces.

Good to know: - the routines use WaitForSeconds, so they freeze with Time.timeScale = 0; - StartWrite() calls StopAllCoroutines() on this GameObject: do not park unrelated coroutines on the same object; - reverse-from-end works on maxVisibleCharacters; reverse-from-start works on per-character vertex alpha, so it keeps the tail of the sentence in place instead of reflowing it.


Inspector Controls

The custom inspector adds a TextTyper Controls block: a Quick Message field (with Copy Current Message and Apply Quick Message, which also previews the text in Edit Mode) and the buttons Start Write, Reverse (From End), Reverse (From Start), Stop Typing — enabled in Play Mode — plus Reset Visible Characters, Reset All, Enable/Disable Component and the two Play On Start: ON/OFF shortcuts, which work in Edit Mode too.

Above them sits the Timing (NEW rule) section with a live Computed readout — Step per char, FadeDuration, Effective Fade (the fade actually applied, clamped to the step), Start Delay and the estimated total duration of the message. It is the fastest way to see that a fade longer than the step is silently cut.

Everything the buttons change in Edit Mode goes through Undo.RecordObject, so Ctrl+Z puts it back.


TextAnimatorUGTK integration

Mn_TextTyperComponent can delegate its typing to TextAnimatorUGTK, gaining tags, effects and inline actions while keeping the classic API and inspector events untouched.


Testing Scene

Open Debug/TextTyperSystemTest.unity and press Play: the TextTyper prefab types the Lorem ipsum message character by character with its 0.2 s step and 0.2 s fade:

The scene shows the reference setup for the typewriter click: the TextTyperBox instance has its onCharacterTyped wired to Mn_AudioUserRandomClips.Play from the Audio System. That is exactly why the per-character events skip whitespace — the click would sound wrong on spaces.


Technical Info

Path Content
Scripts/Runtime/S_WritingSettings.cs Serializable struct: timePerCharacter, startDelay, fadeDuration
Scripts/Runtime/Mn_TextTyperComponent.cs The component: forward/reverse routines, instant path, UnityEvent-friendly API, TextAnimatorUGTK adapter
Scripts/Editor/Mn_TextTyperComponent_Editor.cs Custom inspector: Timing section with computed values, Quick Message, playback buttons
Prefabs/TextTyper.prefab TMP text + Mn_TextTyperComponent pre-configured
Prefabs/TextTyperBox.prefab The same typer inside a ScrollRect with background and scrollbar
Debug/TextTyperSystemTest.unity Test scene: Canvas + TextTyper

Mn_TextTyperComponent — main API: StartWrite(), StartReverseWrite(), StartReverseWriteFromStart(), StopTyping(), ResetVisibleCharactersToZero(), PrepareIdleInvisibleImmediate(), ChangeMessage(), SetMessage*(), SetTimePerCharacter*(), SetStartDelay*(), SetFadeDuration*(), SetWritingSettings(), SetInstantTypingOn()/Off(), ConfigureTextAnimator(), IsUsingTextAnimator(), IsTyping(), GetTypingState() and the Get* family. E_TypingState reports the routine in progress.


© 2026 Marcello De Bonis. All rights reserved

Real dependenciesOpen in the map →

Depends on 1

Used by 1

Measured from the repository: code references (asmdef) plus prefab and asset GUIDs. Importing this module alone brings in 1 module in total.