How to Migrate a Unity Project from TextMesh Pro to UniText

Last reviewed

Short answer

UniText ships a migration tool: Tools → LightSide → UniText → Migration scans a Unity project for TextMesh Pro and converts it in four ordered stages — fonts, components, scripts with their assembly definitions, and a checklist of what is left by hand. It repairs the references, UnityEvent calls and prefab overrides that pointed at the old components, and it records every setting it could not carry over.

It converts the batches you select, or the whole project in one pass, and nothing runs on its own: not during a build and not on project open. The tool is part of the licensed versions of UniText; the free UniText 1.0.0 does not include it.

Before you start

RequirementWhy
Asset Serialization set to Force TextThe scanner reads scenes and prefabs as text. Binary assets would report no TextMesh Pro usage, so scanning is refused until this is set.
A commit in version controlPrefabs, scenes and scripts are rewritten in place. The diff is the real undo.
TextMesh Pro still installedComponents are read through the TextMesh Pro assemblies. Remove the package only after the last finding is handled.
A short-lived branchA migration that runs for weeks collides with everything else in the project.
A Style preset in Project Settings → LightSide → UniTextUniText has no built-in tag vocabulary, so without a project-wide preset migrated text shows <b> and <color> as literal characters. The Dashboard builds one preset carrying every TextMesh Pro tag UniText can reproduce.

The scan reads everything under Assets/ and Packages/: scenes, prefabs, C# files, .asset, .mat, .anim and .asmdef files, and .csv, .json or .txt files that carry markup. Files are matched on worker threads, and every later scan re-reads only the files that changed.

The four stages

The Dashboard shows the stages in order and marks the current one. Each stage depends on the one before it.

  1. Fonts. Every TextMesh Pro font gets a UniText font and a font stack built from its .ttf or .otf file, and the TMP font assets stay where they are. Both fallback lists are rebuilt: a font asset's own fallback table becomes the families behind its stack, and the project-wide list from TMP_Settings becomes one shared fallback stack chained onto every mapped stack. Components wait until every font is mapped or skipped, because a migrated component points at a stack.
  2. Components. Prefabs first, nested prefabs before the prefabs that contain them, scenes last; the tool computes this order from the prefab dependency graph. Migrating one row rewrites every TextMesh Pro component in that prefab or scene. A sibling component that requires a Graphic, such as a shadow or outline effect, is set aside for the swap and put back with its values. A sibling that requires TMP_Text itself, such as a TMP-specific text animator, is removed, and its full serialized state is recorded under Not carried over.
  3. Scripts and assembly definitions. Scripts come after components: a field retyped to UniText has nothing to point at while its scene still holds the TextMesh Pro component. An assembly definition that references Unity.TextMeshPro is pointed at UniText before its scripts are rewritten, so they compile.
  4. Cleanup. Materials on TMP shaders, animation curves, text assets with TMP markup and compiled assemblies are listed for manual work, and the tool records when each one is handled.

Run it in one pass on the Dashboard builds every font that has a source file, migrates every pending component and rewrites every pending script, in that order. It names every count before it starts and stops at the first stage it cannot finish, with the reason.

What the components become

TextMesh ProUniText
TextMeshProUGUIUniText
TextMeshPro (world space)UniTextWorld
TMP_SubMesh, TMP_SubMeshUIRemoved: UniText needs no sub-meshes
TMP_InputFieldUniTextSelectable and UniTextEditable on the field's text object
TMP_DropdownNot converted: rebuild it from Unity's Dropdown with a UniText label

Carried over directly: text, font size with the auto-size range, color, word wrap, auto-size, raycast target, maskable, and the margins, which become UniText's padding. Alignment splits into HorizontalAlignment and VerticalAlignment; TMP's Left and Right become Start and End, which follow each paragraph's writing direction, and Center, Top, Middle, Bottom and Justified map exactly.

Settings that TextMesh Pro keeps on the component and UniText expresses as modifiers become whole-text Style entries on the component: bold, italic, underline, strikethrough, uppercase, lowercase, small caps, character, word, line and paragraph spacing, right-to-left direction, and the ellipsis and truncate overflow modes. The spacing values are measured on different scales, so the Log flags each one for a visual check. fontWeight becomes a position on the variable-font wght axis; with a static font the weight is logged and lost.

Input fields

A TMP_InputField migrates as one composite object or not at all: its text and placeholder convert together with it, so a field is never left with UniText text inside a TMP field. A serialized reference to the old field is redirected to the new UniTextEditable, and the field's settings become input behaviors:

TMP settingUniText
Content Type Integer or DecimalIntegerFilter
Character LimitLengthLimitBehavior, in UTF-16 units
Keyboard Type, Auto-CorrectNativeKeyboardBehavior
Input Type PasswordPasswordBehavior
Line Type Single LineSingleLineBehavior: Enter submits
On Focus Select AllSelectAllOnFocusBehavior
Restore On ESCRestoreOnCancelBehavior
PlaceholderPlaceholderDecorator

A field is left untouched and listed for review when one of its settings has no exact UniText equivalent — a persistent UnityEvent listener, a custom input validator, a line limit, a scrollbar or a customized caret, among others. The review names the condition that stopped it.

Rich text

Markup stays as written. The project-wide Style preset that the Dashboard builds supplies every TextMesh Pro tag UniText reproduces, including <b>, <i>, <u>, <s>, <mark>, <sub>, <sup>, <smallcaps>, <size>, <color>, <alpha>, <font>, <align>, <cspace>, <indent>, <line-height>, <nobr>, <br> and <link>. It covers the whole vocabulary rather than the tags the scan found, so text that arrives at runtime from localization or scripts renders too.

  • Sprite tags keep their numbers: <sprite=15> stays <sprite=15> and shows the same image from a generated UniTextSprites catalog.
  • <material=>, <quad=> and <gradient=> work once you create their Style entry, because the asset they point at cannot be guessed.
  • <pos=>, <space=>, <voffset=>, <rotate=>, <width=>, <noparse>, <page>, <style=>, <margin=> and <margin-right=> have no UniText modifier, so text that uses them has to be rewritten.

How scripts are rewritten

The C# rewrite reads each file lexically before it edits anything. String literals, comments and #if regions gated on a TextMesh Pro symbol are never touched, and a member is renamed only where the file shows that its receiver holds a TextMesh Pro type.

TextMesh Pro typeUniText type
TextMeshProUGUIUniText
TextMeshProUniTextWorld
TMP_TextUniTextBase
TMP_InputFieldUniTextEditable
TMP_FontAssetUniTextFont

Members on those receivers are renamed too: .text becomes .Text, .fontSize becomes .FontSize, .enableAutoSizing becomes .AutoSize and .enableWordWrapping becomes .WordWrap. A literal alignment assignment splits into the two alignment properties, onValueChanged.AddListener(h) becomes ValueChanged += (h) because UniText raises C# events, and using TMPro; becomes using LightSide;.

Before:

csharp
using TMPro;
using UnityEngine;

public class ScoreLabel : MonoBehaviour
{
    [SerializeField] TextMeshProUGUI label;

    void Start()
    {
        label.alignment = TextAlignmentOptions.TopRight;
        label.text = "0";
    }
}

After:

csharp
using LightSide;
using UnityEngine;

public class ScoreLabel : MonoBehaviour
{
    [SerializeField] UniText label;

    void Start()
    {
        label.HorizontalAlignment = LightSide.HorizontalAlignment.End;
        label.VerticalAlignment = LightSide.VerticalAlignment.Top;
        label.Text = "0";
    }
}

Where a file cannot see a receiver's type, such as a method's return value or a lambda parameter, the member is reported instead. Once the files are applied, each compile error that names a TextMesh Pro member on a UniText receiver is renamed at the position the compiler reports, until the scripts compile, and every rename goes to the Log.

Script Preview shows the diff of each file before it is written. Every applied file keeps its original beside it as .bak, and the Log's Restore puts it back. Members whose meaning changes, such as textInfo, ForceMeshUpdate, GetPreferredValues, margin, overflowMode and the material and outline members, are reported and never rewritten.

References that survive the swap

A replaced component gets a new identity in its file, so the tool repairs references across every text-serialized asset in Assets/ and in embedded or local packages:

  • Fields that pointed at a migrated text, in any scene, prefab or asset and through any depth of nested prefabs and variants, are redirected to the UniText component.
  • Persistent UnityEvent calls are rebound: TextMeshProUGUI.set_text becomes UniText.set_Text. A call with no counterpart stays as it was, and the Log names it.
  • Prefab-instance overrides of text, color, font size, alignment, margins and font move to the matching UniText properties. Overrides that UniText expresses through Styles are listed under Not carried over with what to set instead.
  • A TMP_FontAsset field that became a UniTextFont field keeps its value, pointing at the mapped UniText font.

Until its script is rewritten, a field is still typed for TextMesh Pro, and Unity empties it when the asset is saved. The tool records each such field, and after the script stage the Dashboard offers to restore every one that came back empty. Before it rewrites files, it asks to save open scenes with unsaved changes, and afterwards it reloads every open scene whose file it rewrote, so a stale copy in the editor cannot undo the repairs.

What stays manual

  • TMP_Dropdown: rebuild it from Unity's Dropdown with a UniText label.
  • Materials on TMP shaders: outline, underlay and glow become whole-text StrokeModifier, ShadowModifier and GlowModifier entries that you add.
  • Animation clips: color and font-size curves map onto UniText properties, and every other TMP-specific curve is re-authored.
  • TMP_StyleSheet, TMP_Settings, and compiled assemblies that expose TextMesh Pro types, which only their owner can rebuild against UniText.
  • World-space TextMeshPro components whose text contains sprite tags stay on TextMesh Pro, because UniText's sprites render through Canvas UI. The tool marks them Failed instead of dropping the sprites.

Undo and team work

  • Version control covers everything. Commit before each batch.
  • Component migration in a scene that was already open is one Unity Undo step per scene. Prefabs, and scenes the batch opened itself, are written to disk, and only version control takes them back.
  • Verify re-reads every component marked Completed and returns anything that still has a TextMesh Pro script to Pending, for example after a revert.

Progress lives in ProjectSettings/UniText: MigrationState.json, FontMappings.json and MigrationLosses.json are meant to be committed, so a team works from one checklist and nobody repeats finished work. An optional guard warns whenever a scene or prefab with TextMesh Pro components is imported while the migration is in progress.

Questions

Is the migration tool part of the free version?

No. It ships with the licensed versions of UniText. UniText 1.0.0, the free MIT version, does not include it.

Can a project migrate gradually?

Yes. Components migrate one prefab or scene at a time, and both packages stay installed side by side until the last finding is handled.

What happens to TextMesh Pro font assets?

They stay where they are, with every reference intact. Remove TextMesh Pro yourself once the last component and script are migrated.

Where is the full reference?

The package ships it as Documentation/TmpMigration.md, with every tab, status and complexity grade the tool reports.

Check it in your project

Run the browser demo, follow the setup guide, or compare the free version with the licenses.

Related problems