安装:
下载setup工具,导入到unity中,设置token:


添加unitext到场景:
UniText提供了两个组件:
UniText:用于ui显示文本,通过ugui CanvasRenderer 渲染;
UniTextWorld:用于3d世界显示文本,通过隐形的批处理器合并渲染;
(两个组件共享文本处理流程,只是渲染表面不同)
UniText :
创建UniText:

在pc和移动平台,不需要指定字体,使用操作系统默认的字体渲染(WebGL除外);
UniTextWorld:
创建世界空间Text:

(scale默认为0.01,可设置Sorting Order)
一个隐藏的UniTextWorldBatcher组件将世界text组装合并成一系列mesh,将兼容的组件合并成一个drawcall,遵循unity排序方式;
输入事件由UniTextWorldRaycaster处理,挂载在渲染相机上;
字体:
没有字体指定时,或者字体里面没有相关字符,使用操作系统默认的字体(WebGL除外,不能访问操作系统的字体);
通过组件的Font和FontStack属性指定字体:

可同时,部分或都不指定,优先级:Font > Font Stack > OS fonts;
Font:是一个单独的UniTextFont;
FontStack:
是多个家族集合(有自己的后备链);
用于多语言或加粗/斜体文本;
关键设置:
Render mode:在底层,以两种方式渲染字形

SDF:单通道有向距离场,默认的,圆角显示,支持描边和阴影;
MSDF:多通道有向距离场,用于字体尖角显示,消耗更多图集内存;
(两种方式都使用基于曲线的光栅化,字形存储在一个共享的Texture2DArray图集,有自适应图块尺寸,引用计数和LRU逐出)
Atlas:页面大小以及字形块大小,更大的显示小字形清晰,但耗内存;
Metrics normalization:让不同设计尺寸的字体混合显示时保持在同一基准线上;
Face override:当字体提供错误的数据时,修正ascender/descender/line gap;
创建UniTextFont资源:
-
导入字体(.ttf, .otf或.ttc);
-
选择一个或多个字体;
-
右键菜单创建:


或使用Tools窗口创建(尤其对于那些位于工程外部的字体):


-
Project里选择字体,或者拖入到该窗口,或者打开工程外部文件;
-
点击Create N UniText Font Asset(s);
字体会被直接嵌入到UniTextFont里,因此运行时没有外部文件依赖;
字体设置:
选择一个UniTextFont资源;
Font Scale:视觉缩放,避免字体设计过大或过小;
SDF Detail:贴片细节缩放,更大的值会使用更大的图集图块,用于书法;
Glyph Overrides:每个字形配置,如图块大小(自动/64/128/256);

Face Info:family name, style, weight class, italic flag (read-only, extracted from font data);
Variable Font Axes:可变字体,显示可用的轴;
Font Data Status:字体字节嵌入情况;
Runtime Data:显示字形计数,字符计数;
Atlas Preview:SDF, MSDF, and Emoji atlas texture slices:

创建UniTextFontStack (字体集合):
将字体组织成字体家族,每个家族有:
主字体和可选字体(如粗体,斜体,浅色等),同一家族,不同粗细/样式;
可选的名字,用于标记寻址;
可选的preferredLanguage,一个BCP 47标签,当语言匹配时使码点解析偏向该家族;
(UniTextFontStack可包含多个家族,查找字形时依次搜索)
Font Stack (Combined):
选择多个UniTextFont,右键菜单创建UniTextFontStack:


(字体依据familyName自动分组,最接近规则的字体成为主字体,其他的成为face)
字符码点解析从上往下搜索字体家族,直到找到覆盖码点的字体,没有的话搜索系统字体,然后emoji atlas;
当使用<b>粗体,系统在字体家族中查找最匹配的face,如果没有,应用合成效果(虚假的粗体和斜体);
Font Stack (Combined)使用场景:在一个组件里面显示多语言文本(包含真实的粗斜体);
Font Stack (Per Font):
选择一个UniTextFont,右键创建:


使用场景:不同的组件使用不同的字体;
Variable Fonts(可变字体):
相对于静态字体,可变字体强烈推荐,一个可变字体可替换数十个静态的静态字重/宽度的字体;
可变字体轴通过修饰符控制:
<b> 和 <i>自动设置合适的轴;
也可通过 VariationModifier <var> 直接控制;
三层字形求解(当使用粗体或斜体时,依次使用如下求解):
-
Variable font axes:如果字体有wght/ital/slnt axes,直接设置;
-
Static font faces:在家族中查找最匹配的face;
-
Synthesis :虚假合成,应用假的粗体(SDF dilate)或假的斜体(剪切变换);
Fallback Stack:
UniTextFontStack有一个fallbackStack字段引用另一个UniTextFontStack.;
系统首先搜索每个家族的主要字体,然后走fallbackStack,最后走系统;
UniText Tools窗口:

Create Font Asset:

用于创建字体资源;
Font Subsetter(字体子集):

创建一个优化的字体子集,只保留部分字符;
Dictionary Builder:

对于在单词间不使用空格的东南亚语,需要构建单词分隔字典,正确换行;
生成WordSegmentationDictionary资源,然后配置在Project Settings:

材质球:
基础文本渲染和emoji渲染的材质球被UniTextMaterialCache自动管理;
不能在UniText组件上指定材质球;
描边和阴影效果使用与字形相同的mesh来渲染;
System Fonts:
任何码点没被字体覆盖到的,使用操作系统字体求解,并缓存;
代码关闭系统字体求解:SystemFont.Disabled = true;
UniTextSystemFont asset (explicit):

系统字体从操作系统字符数据;

每个平台可选择一个操作系统必提供的字体,当某个平台没指定时使用Common;
指定的字体没找到的话,则使用系统必提供的字体;
每个平台可设置字形特性以及sdf/tile,未指定时使用从字体中读取的值;
用于匹配系统ui字体,或者依赖系统字体减小构建体积;
Font Variants(字体变体):
使用其他字体的原始字节,但拥有自己的face metrics, render settings, 和glyph overrides;
Markup System:
UniText基于Modifiers和Parse Rules,提供了一个可扩展的标记系统;
架构:Rule + Modifier:
Parse Rule (IParseRule) :在文本中查找指定模式,生成一个范围;
Modifier (BaseModifier):对查找到范围应用视觉或结构效果;
tags和modifiers没有硬关联,任何parse rule可以驱动任何modifier;
Built-in Modifiers(约定的,而非强制):



自定义Tags,使用默认参数:
使用 TagRule 创建自定义tags,通过DefaultParameter属性指定预设值;
cs
TagRule tagRule = new TagRule("warning") { DefaultParameter = "#FF0000" };
<warning>error occurred</warning> // 使用默认的颜色值
<warning=#FFA500>caution</warning>
对于有多个参数的modifiers,未提供的值使用预设值;
cs
TagRule tagRule = new TagRule("glow") { DefaultParameter = "0.3,#00FF00" };
<glow>text</glow> // dilate 0.3, green outline
<glow=0.5>text</glow> --- dilate 0.5, green outline
Parse Rule Types:
Tag-Based Rules:
使用TagRule类,提供一个tag名,以及可选的参数;
Markdown-Style Rules:

Utility Rules:
RangeRule:对特定字符范围应用修饰符,无需任何标记;
StringParseRule:匹配并可选地替换纯文本模式;
CompositeParseRule:
将多个规则组合一起;
文本每个位置依次根据子规则进行检查,直到匹配;
Protection Rules (standalone):
保护某部分内容不被其他解析规则处理;
实现了IParseRule.IsStandalone = true,单独的,不需要搭配modifier;
文本保持不变,只是其中的分隔符被去掉了;

cs
_UniText.AddRule(new NoparseTagRule());
_UniText.AddRule(new BackslashEscapeRule());
//_UniText.RemoveRule(rule);
(AddRule 需要IParseRule.IsStandalone == true)
Parameter Formats Reference:
modifier参数语法:
color (ColorModifier):
Hex: #RGB, #RRGGBB, #RRGGBBAA;
Named:hite, black, red, ...
size (SizeModifier):
Absolute: <size=24> // 24 pixels;
Percentage: <size=150%> // 150% of base size
Relative: <size=+10> / <size=-5> // offset from base size
gradient (GradientModifier):
Format: <gradient=name,shape,angle>
Shapes: linear (default), radial, angular
Angle: 0--360 degrees (0=right, 90=up). Used by linear and angular
(gradients定义在UniTextGradients资源,Project Settings → UniText → Gradients)
cspace (LetterSpacingModifier):
Format: spacing,monospace
Pixels: <cspace=5> // 5px extra spacing
Em units: <cspace=0.1em> // 0.1 em extra spacing
Monospace: <cspace=0.5em,true> // 所有字形具有相同的宽度
outline (OutlineModifier):
<outline> // default (black, dilate=0.2)
<outline=#FF0000> // custom color
<outline=,0.3> // custom dilate, default color
<outline=#FF0000,0.3> // both (color, dilate)
<outline=rainbow> // gradient outline(modifier需要一个GradientProvider)
<outline=rainbow,0.3,radial,45> // gradient + dilate + shape + angle
shadow (ShadowModifier):
<shadow> // default (black with 50% alpha)
<shadow=#00000080>
<shadow=0.1,#000,2,2,0.5> --- dilate, color, offsetX, offsetY, softness
var (VariationModifier):
位置轴值依次为:wght, wdth, ital, slnt, opsz
使用 ~ 跳过一个轴;
Absolute: <var=700> // weight 700
Percentage: <var=150%> // 150% of default weight
Delta: <var=+200> // +200 from default weight
Multiple axes: <var=700,80> // weight 700, width 80
Skip axes: <var=~,~,~,-12> // only set slant to -12
ellipsis (EllipsisModifier):
<ellipsis=1> // truncate end (default): Hello Wo...
<ellipsis=0> // truncate start: ...o World
<ellipsis=0.5> // truncate middle: Hel...rld
Any float 0--1 for fine-grained control
font (FontModifier):
参数为组件font stack里的FontFamily.name;
<font=pixel>Score</font> // 用pixel字体
lang (LanguageModifier):
参数为一个 BCP 47 标签;
<lang=zh-Hans>汉字</lang>
mat (MaterialModifier):
参数为颜色值;
<mat>text</mat> // 直接使用材质球的颜色
<mat=#FF8800>text</mat> // 顶点颜色乘以指定的颜色
添加 Styles 到组件:
Inspector:

选择一个预设,Rule和Modifier自动配置好;

规则中的tag会在文本中解析出来,Modifier会对匹配的范围应用效果;
Via Code:
cs
// Whole-text application (equivalent to RangeRule with ".."):
uniText.Styles.Add(Style.WholeText(new ColorModifier(), "#FF6600"));
// Fixed codepoint range:
uniText.Styles.Add(Style.Range(new ColorModifier(), start: 0, end: 5, parameter: "#FF0000"));
// Tag-based:
uniText.Styles.Add(Style.Tag(new ColorModifier(), "color"));
uniText.Styles.Add(Style.Tag(new ColorModifier(), "warning", defaultParameter: "#FF0000"));
自由组合 rule 和 modifier:
cs
uniText.Styles.Add(new Style
{
Source = new TagRule("color"),
Modifier = new ColorModifier()
});
查询和修改Styles:
cs
// Check presence
bool hasBold = uniText.HasModifier<BoldModifier>();
// Find the first style backed by a given modifier type
if (uniText.TryGetStyle<ColorModifier>(out var colorStyle)) {}
// Enumerate every matching style (local + preset copies)
foreach (var s in uniText.GetStylesOfType<LinkModifier>()) {}
// Whole-text convenience --- add/update/toggle/clear a style that covers the full text
uniText.SetWholeText<BoldModifier>(); // bold everything
uniText.SetWholeText<ColorModifier>("#FF0000"); // red everything
bool isBold = uniText.ToggleWholeText<BoldModifier>(); // invert
string currentColor = uniText.GetWholeTextParameter<ColorModifier>();
uniText.ClearWholeText<ColorModifier>();
(都是操作组件本地的styles,不修改Style Preset(多个组件共享))
Style Preset(共享配置):
多个组件需要一组相同的modifiers,每个组件手动设置繁琐和易出错;
使用Style Preset(ScriptableObject )存储一组重复使用的Rule+Modifier;
使用流程:
- 创建Style Preset;

- 添加Rule+Modifier队:

- 设置组件的Style Presets

preset Styles和local Styles共同作用于文本;
cs
uniText.AddStylePreset(myPreset);
bool removed = uniText.RemoveStylePreset(myPreset);
uniText.ClearStylePresets();
RangeRule :
用于程序化指定文本范围,不需要标签;
应用所有文本,使用范围"..":
cs
uniText.Styles.Add(Style.WholeText(new ColorModifier(), "#FF0000"));
// Explicit form:
var rangeRule = new RangeRule();
rangeRule.data.Add(new RangeRule.Data
{
range = "..", // ".." means the full text range
parameter = "#FF0000"
});
uniText.AddStyle(new Style { Rule = rangeRule, Modifier = new ColorModifier() });
范围语法(使用c#风格的范围表示):

多个范围:
cs
var rangeRule = new RangeRule();
rangeRule.data.Add(new RangeRule.Data { range = "0..5", parameter = "#FF0000" });
rangeRule.data.Add(new RangeRule.Data { range = "10..20", parameter = "#00FF00" });
uniText.AddStyle(new Style { Rule = rangeRule, Modifier = new ColorModifier() });
// Codepoints 0-4 are red, 10-19 are green
StringParseRule(文字模式匹配):
cs
var emojiRule = new StringParseRule();
emojiRule.patterns = new[] { ":)", ":(", ":D" };
emojiRule.hasReplacement = true;
emojiRule.replacement = "😊";
uniText.AddStyle(new Style
{
Rule = emojiRule,
Modifier = new EmptyModifier() // no visual effect, just replacement
});
// ":)" in text gets replaced with "😊"
CompositeParseRule(组合多个规则) :
依次尝试每个子规则,直到第一个匹配;
cs
var composite = new CompositeParseRule();
composite.rules.Add(new TagRule { tagName = "link" }); // <link=url>text</link>
composite.rules.Add(new MarkdownLinkParseRule()); // [text](url)
composite.rules.Add(new RawUrlParseRule()); // auto-detect https://...
uniText.AddStyle(new Style
{
Rule = composite,
Modifier = new LinkModifier()
});
// All three link syntaxes work with a single modifier
优先级制度:
Rule有个Priority属性控制匹配顺序,值越大越先匹配;
正值:自定义rule,优先匹配;
0(默认值):标准的tag和markdown Rule;
负值: 备选匹配,如RawUrlParseRule (-100);
(如<link=url>https://example.com</link>不会同时被TagRule和RawUrlParseRule匹配)
创建自定义Parse Rule:
实现IParseRule:
cs
public interface IParseRule
{
int Priority => 0;
bool IsStandalone => false; // true = register without a modifier (protection rules)
int TryMatch(ReadOnlySpan<char> text, int index, PooledList<ParsedRange> results);
void Finalize(ReadOnlySpan<char> text, PooledList<ParsedRange> results) { }
void Reset() { }
}
或者使用TagRule:
如果遵循<tag>content</tag>语法模式,使用内置的TagRule,提供自己的tag name:

创建自定义modifiers:
UniText有几个基类modifier,用于不同的场景;
Text Transformation (BaseModifier):
用于在渲染前码点转换,如uppercase;
cs
[Serializable]
public class LowercaseModifier : BaseModifier
{
protected override void OnEnable() { }
protected override void OnDisable() { }
protected override void OnDestroy() { }
protected override void OnApply(int start, int end, string parameter)
{
var codepoints = buffers.codepoints.data;
var count = buffers.codepoints.count;
var clampedEnd = Math.Min(end, count);
for (var i = start; i < clampedEnd; i++)
codepoints[i] = char.ToLowerInvariant((char)codepoints[i]);
}
}
Per-Glyph Visual Effect (GlyphModifier<T>):
在mesh构建时更改字形外观,如color, underline等;
cs
[Serializable]
public class HighlightModifier : GlyphModifier<byte>
{
[SerializeField] private Color highlightColor = Color.yellow;
protected override string AttributeKey => "highlight";
protected override Action GetOnGlyphCallback() => OnGlyph;
protected override void DoApply(int start, int end, string parameter)
{
var buffer = attribute.buffer.data;
buffer.SetFlagRange(start, Math.Min(end, buffers.codepoints.count));
}
private void OnGlyph()
{
var gen = uniText.MeshGenerator;
if (!attribute.buffer.data.HasFlag(gen.currentCluster))
return;
var colors = gen.Colors;
var baseIdx = gen.faceBaseIdx; // stable index of the face quad for this glyph
colors[baseIdx] = colors[baseIdx + 1] =
colors[baseIdx + 2] = colors[baseIdx + 3] = highlightColor;
}
}
(使用gen.faceBaseIdx访问当前字形的face quad,不要使用gen.vertexCount - 4,其他modifier可能添加顶点)
Effect Quads (EffectModifier):
重复几何渲染,如outline, shadow, glow;
cs
[Serializable]
public class MyGlowModifier : EffectModifier
{
[SerializeField] private Color glowColor = Color.cyan;
[SerializeField] private float dilate = 0.3f;
protected override void OnGlyphEffect()
{
var gen = uniText.MeshGenerator;
if (gen.font.IsColor) return; // skip emoji
var baseIdx = gen.faceBaseIdx;
var packed = EffectPacking.PackColor(glowColor);
EnqueueEffectQuad(
baseIdx,
new Vector4(dilate, packed.x, packed.y, 0f),
expandDelta: 0f);
}
}
EnqueueEffectQuad记录一个额外的四边形渲染请求;
Sub-mesh With Its Own Material (SubMeshModifier):
需要使用其他的材质球或shader(如MaterialModifier),继承SubMeshModifier;
Interactive Region (InteractiveModifier):
可点击或悬浮的文本区域,事件监听:
cs
[Serializable]
public class HashtagModifier : InteractiveModifier
{
public override string RangeType => "hashtag";
public override int Priority => 50;
public event Action<string> HashtagClicked;
protected override void OnApply(int start, int end, string parameter)
{
AddRange(start, end, parameter); // Register clickable region
}
protected override void HandleRangeClicked(InteractiveRange range, TextHitResult hit)
{
HashtagClicked?.Invoke(range.data);
}
protected override void HandleRangeEntered(InteractiveRange range, TextHitResult hit) { }
protected override void HandleRangeExited(InteractiveRange range) { }
}
Modifier生命周期:

自定义modifier最佳实践:
不要创建T\[\]:
使用UniTextArrayPool<T>.Rent/Return,或buffers.GetOrCreateAttributeData<T>();
Subscribe in OnEnable, unsubscribe in OnDisable;
使用PrepareForParallel(),对于任何调用unity api(如Material.GetFloat(), transform reads)
通过gen.faceBaseIdx访问face quad,不要使用gen.vertexCount - 4;
跳过color (emoji) glyphs处理(gen.font.IsColor);
交互式文本:
UniText提供了如下支持:可点击区域,悬停检测以及视觉反馈;
Click and Hover Events:
cs
// Any text click
uniText.TextClicked += hit => Debug.Log($"Clicked cluster: {hit.cluster}");
// Interactive range events (links, custom ranges)
uniText.RangeClicked += hit => Debug.Log($"Clicked: {hit.range.data}");
uniText.RangeEntered += hit => Debug.Log($"Hover enter: {hit.range.data}");
uniText.RangeExited += hit => Debug.Log($"Hover exit: {hit.range.data}");
// Continuous hover tracking
uniText.HoverChanged += hit => Debug.Log($"Hover at cluster: {hit.cluster}");
碰撞检测:
cs
// Local space
TextHitResult hit = uniText.HitTest(localPosition);
// Screen space
TextHitResult hit = uniText.HitTestScreen(screenPosition, eventCamera);
// Get visual bounds for a cluster range
var bounds = new List<Rect>();
uniText.GetRangeBounds(startCluster, endCluster, bounds);
文本高亮显示:
Highlighter属性控制 点击,悬停以及程序化选择的视觉反馈;
cs
if (uniText.Highlighter is DefaultTextHighlighter highlighter)
{
highlighter.ClickColor = new Color(1, 0, 0, 0.5f);
highlighter.HoverColor = new Color(0, 0, 1, 0.1f);
highlighter.SelectionColor = new Color(0.3f, 0.6f, 1f, 0.3f);
highlighter.FadeDuration = 0.5f;
// Programmatic selection (e.g., for searching, cursor, etc.)
highlighter.SetSelection(startCluster: 10, endCluster: 20);
highlighter.ClearSelection();
}
// Disable highlighting entirely
uniText.Highlighter = null;
自定义高亮:
扩展TextHighlighter (or DefaultTextHighlighter);
UniTextWorldRaycaster:
对于世界文本,添加 UniTextWorldRaycaster 组件到摄像机来获取点击事件;
组件 RaycastTarget 默认为false;

文本解析器(IUniTextResolver):
用于替换组件的原始文本;
cs
public class LocalizationResolver : IUniTextResolver
{
private UniTextBase owner;
private Action<string> onLanguageChanged;
private Dictionary<string, string> table;
public void OnAttached(UniTextBase owner)
{
this.owner = owner;
onLanguageChanged = _ => owner.SetDirty(UniTextDirtyFlags.Text);
LocalizationSignal.LanguageChanged += onLanguageChanged;
}
public void OnDetached(UniTextBase owner)
{
if (onLanguageChanged != null)
LocalizationSignal.LanguageChanged -= onLanguageChanged;
onLanguageChanged = null;
this.owner = null;
table = null;
}
public void PrepareForParallel()
{
// Cache main-thread-only values here --- TryResolve below may run off-thread.
table = LocalizationTables.GetTable(LocalizationSignal.CurrentLanguage);
}
public bool TryResolve(ReadOnlyMemory<char> source, out ReadOnlyMemory<char> result)
{
var key = source.ToString();
if (table != null && table.TryGetValue(key, out var translated))
{
result = translated.AsMemory();
return true;
}
result = default;
return false;
}
}
uniText.TextResolver = new LocalizationResolver();
uniText.Text = "greeting.hello"; // serialized key; rendered as the localized translation
// Later, to detach:
uniText.TextResolver = null; // OnDetached is called automatically, signal is unsubscribed
(TryResolve 可能在工作线程调用)
语言国际化:
使用一个BCP 47语言标签,有3种情况依赖该标签:
- OpenType字体本地化特征:
全中文字体能够正确显示汉字的地域形式,如简体中文,繁体中文,日文,韩文;
- FontFamily.preferredLanguage:
码点到字体解析,字体家族preferredLanguage与当前标签匹配优先使用;
- 自定义modifier,通过AttributeKeys.Language读取每个码点的字体;
3个地方设置语言:
优先级从上往下
Per-range:<lang=...>...</lang>或Style.Tag / Style.Range / Style.WholeText;
Per-component:uniText.Language = "zh-Hans"
实际在组件的local Styles list查找或创建一个 whole-text LanguageModifier style;
Project-wide:UniTextSettings.Language (Project Settings>UniText>Localization>Language)
范围语言标签:
cs
// Register the modifier once (either directly, via a preset, or on a Style Preset asset):
uniText.Styles.Add(Style.Tag(new LanguageModifier(), "lang"));
// Then in text:
uniText.Text = "日本語: <lang=ja>骨</lang>, 中文简: <lang=zh-Hans>骨</lang>, 中文繁: <lang=zh-Hant>骨</lang>";
通过language选择正确字体:
在字体家族种指定preferredLanguage;

当UniText.Language = "zh-Hans",码点首先在SC家族解析,没有匹配的,则按正常链匹配;
字体家族命名:
给每个字体家族一个易于识别的名称,然后标签使用;

cs
uniText.AddStyle(Style.Tag(new FontModifier(), "font"));
uniText.Text = "Score: <font=pixel>100</font> <font=icons>♥</font>";
匹配的字体优先级高于preferredLanguage以及默认的后备链;
如果匹配的字体不能解析码点,则按正常的后备链流程处理;
自定义Materials & Shaders:
MaterialModifier通过发射专门的子网格来应用任何材质球到指定的文本;
可用于溶解效果,全息着色,火焰文本等自定义sdf效果;
使用UniText提供的材质球:

在inspector设置MaterialModifier:

代码设置:
cs
var mat = new MaterialModifier { Material = myDissolveMaterial };
uniText.Styles.Add(Style.Tag(mat, "mat")); // pick any name; "mat" is just the convention
uniText.Text = "Hello <mat>dissolving</mat> world!";
自定义shader:


示例shader (UniText/Shaders/Templates/Examples/)
UniText/Custom/Dissolve
UniText/Custom/Hologram
UniText/Custom/Rainbow
组合模式:
MaterialModifier.renderOrder控制自定义材质与基础文本pass在指定范围内的组合模式;
Replace (default):Base SDF pass被抑制(强制alpha为0),只自定义材质渲染;
在onGlyph回调强制alpha归0;
UniText依次调用onGlyph回调,按style出现在组件style list里的顺序;
如果在MaterialModifier后有ColorModifier / GradientModifier,则会修改归零的alpha;
Over:自定义材质渲染在基础文本上面;
Under:自定义材质渲染在基础文本下面;
逐文本和逐字符shader数据:
Per-text constants:
ConstantUv2 / ConstantUv3 (Vector4 each);
对应modifier子mesh的顶点数据TEXCOORD2 / TEXCOORD3;
Per-glyph writer:
在glyphDataWriter 回调里构建子mesh时,逐字符设置uv2 / uv3;
Emoji material slot:
使用指定的material代替默认的 emoji pass来渲染emoji;
Noise texture generator:

(生成无缝灰度图噪声)
世界文本光照计算:
UniText/Lit/SDF;
UniText/Lit/Emoji
RTL and Bidirectional Text:
UniText自动处理:
RTL scripts (Arabic, Hebrew):从右往左;
BiDi mixing:混合双向文本正确显示;
Complex shaping:阿语连字符,印度语连字符等(通过HarfBuzz)
方向设置:
Auto (default) :采用第一个强方向字符的方向;
LeftToRight:force left-to-right;
RightToLeft:force right-to-left;
Emoji:
emoji自动显示,系统emoji字体自动检测使用;
emoji以color bitmaps(位于单独的图集)渲染;
对于表情符号表示码点,首先检查 emoji 字体,然后回退到正常字符堆栈;
Text Model:
各阶段字符串:

UniText组件属性类型:

TextOverrideSource flags:表示渲染的文本与原始text有区别的原因:
None, SetText, Resolver, or a combination;
除了Text,其他都是零gc,复用池中数组;
运行时设置文本:
cs
// 1) Standard --- writes to the serialized field (scene/prefab becomes dirty).
uniText.Text = "Hello";
// 2) Zero-alloc buffer assignment --- does NOT touch the serialized field, no dirty flag.
char[] buffer = ...;
uniText.SetText(buffer, offset: 0, length: 5);
// 3) Zero-alloc memory assignment --- same semantics as (2).
ReadOnlyMemory<char> mem = "Hello".AsMemory();
uniText.SetText(mem);
uniText.SetText("Hello"); // convenience overload (null → empty)
uniText.SetText不会修改序列化属性Text;
检查文本:
渲染文本调试信息;
编辑器:鼠标停留在UniText / UniTextWorld上显示调试信息,按P锚住;

Play mode / player builds:按F8开启,P锚住;
代码控制:
cs
UniTextInspector.Enable(); // also Toggle() / Disable()
UniTextInspector.Layers = InspectionLayers.GlyphBox | InspectionLayers.RunBounds; // [Flags]
UniTextInspector.Filter = InspectionFilter.Fallback; // mark fallback glyphs (also Notdef, Rtl)
UniTextInspector.ShowBiDi = true; // direction arrows on each visual run
UniTextInspector.ShowStats = true; // whole-component card: chars, glyphs, runs, fonts, scripts
UniTextInspector.Target = myText; // pin to one component; null = whatever is under the cursor
Common Properties:
UniText通用的属性;
代码示例:
基础使用:
cs
uniText.Text = "Hello, World!";
uniText.FontSize = 24;
uniText.HorizontalAlignment = HorizontalAlignment.Center;
可点击链接:
cs
var linkModifier = new LinkModifier();
linkModifier.AutoOpenUrl = false;
uniText.Styles.Add(Style.Tag(linkModifier, "link"));
uniText.Text = "Visit <link=https://example.com>our website</link> for more info.";
linkModifier.LinkClicked += url => Application.OpenURL(url);
linkModifier.LinkEntered += url => Debug.Log($"Hovering: {url}");
linkModifier.LinkExited += () => Debug.Log("Left link");
uniText.AddStyle(new Style { Modifier = new LinkModifier(), Rule = new MarkdownLinkParseRule() });
uniText.Text = "Visit [our website](https://example.com) for details.";
uniText.AddStyle(new Style { Modifier = new LinkModifier(), Rule = new RawUrlParseRule() });
uniText.Text = "Check https://example.com for updates.";
嵌入对象 (图文并排):
cs
// Requires: ObjModifier + TagRule("obj") registered
// ObjModifier must have an InlineObject named "coin" with a RectTransform prefab
uniText.Text = "You earned <obj=coin/> 100 gold!";
List:
cs
// With MarkdownListParseRule + ListModifier registered:
uniText.Text = "Shopping list:\n- Apples\n- Bananas\n- Oranges";
// Ordered list:
uniText.Text = "Steps:\n1. Open app\n2. Click button\n3. Done";
Apply Color to Entire Text (RangeRule):
cs
uniText.AddStyle(Style.WholeText(new ColorModifier(), "#FF6600"));
uniText.Text = "This entire text is orange.";
Whole-text:
cs
uniText.SetWholeText<BoldModifier>(); // make everything bold
uniText.SetWholeText<ColorModifier>("#FF0000"); // everything red
bool isBold = uniText.ToggleWholeText<BoldModifier>();
uniText.ClearWholeText<ColorModifier>();
Language and font switching:
cs
// Project-wide default
UniTextSettings.Language = "zh-Hans";
// Per-component
uniText.Language = "ja";
// Per-range (requires LanguageModifier registered):
uniText.AddStyle(Style.Tag(new LanguageModifier(), "lang"));
uniText.Text = "日: <lang=ja>骨</lang> 中: <lang=zh-Hans>骨</lang>";
// Named font families (requires FontModifier registered):
uniText.AddStyle(Style.Tag(new FontModifier(), "font"));
uniText.Text = "Score: <font=pixel>100</font>";
Emoji:
cs
uniText.Text = "Hello! 👋 Great job! 🎉";
World-Space text:
cs
public class WorldLabel : MonoBehaviour
{
[SerializeField] private UniTextWorld label;
void Start()
{
label.Text = "Target <color=red>acquired</color>";
label.SortingOrder = 10;
label.FontSize = 48;
label.RangeClicked += hit => Debug.Log($"Clicked: {hit.range.data}");
}
}
// Make sure Camera.main has a UniTextWorldRaycaster (added automatically by the menu).
Custom Material via MaterialModifier:
cs
var mat = new MaterialModifier { Material = myDissolveMaterial };
uniText.AddStyle(Style.Tag(mat, "mat"));
uniText.Text = "Attacked: <mat>*HIT*</mat>";
// Animate a shader parameter (e.g., dissolve progress) via the per-text UV:
void Update()
{
mat.ConstantUv2 = new Vector4(Mathf.PingPong(Time.time, 1f), 0, 0, 0);
}
System fonts (no bundled font):
cs
// No FontStack assigned --- renders with the OS default font, gaps fill from OS fonts (§2.6).
// Works on desktop and mobile; on WebGL assign a regular UniTextFont instead.
uniText.Text = "Uses the operating system font 你好 مرحبا";
SystemFont.Disabled = true; // opt out of OS fallback --- uncovered codepoints show missing-glyph boxes