Unity UniText笔记

安装:

下载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资源:

  1. 导入字体(.ttf, .otf或.ttc);

  2. 选择一个或多个字体;

  3. 右键菜单创建:

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

  1. Project里选择字体,或者拖入到该窗口,或者打开工程外部文件;

  2. 点击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> 直接控制;

三层字形求解(当使用粗体或斜体时,依次使用如下求解):

  1. Variable font axes:如果字体有wght/ital/slnt axes,直接设置;

  2. Static font faces:在家族中查找最匹配的face;

  3. 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;

使用流程:

  1. 创建Style Preset;
  1. 添加Rule+Modifier队:
  1. 设置组件的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种情况依赖该标签:

  1. OpenType字体本地化特征:

全中文字体能够正确显示汉字的地域形式,如简体中文,繁体中文,日文,韩文;

  1. FontFamily.preferredLanguage:

码点到字体解析,字体家族preferredLanguage与当前标签匹配优先使用;

  1. 自定义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
相关推荐
鼎艺创新科技13 小时前
不依赖 UE/Unity:我们如何从零搭建一套国产三维 GIS 渲染引擎
人工智能·算法·unity·游戏引擎·三维电子沙盘
五仁烧饼16 小时前
Unity性能优化系列设置篇 - URP 移动端设置基线与分档策略
unity·游戏引擎
新手unity自用笔记20 小时前
unity基于Socket的网络学习
网络·网络协议·学习·unity·c#·游戏引擎
心前阳光1 天前
Unity之XR Interaction Toolkit | IPointerDownHandler问题
unity·游戏引擎·xr
帅_shuai_2 天前
unity 静态字段内存查找工具
unity·游戏引擎
鹿野素材屋3 天前
Unity超轻量级中文语音播报,仅5兆大小,无需联网即可使用,适用于弹幕、提示等动态语音播出
unity·游戏引擎
玖玥拾4 天前
Unity3D RPG 入门项目(八)游戏设置面板、帧率控制、快捷技能药品栏、技能解锁系统
游戏·3d·unity·游戏引擎
ellis19704 天前
u3d插件xLua[十] lua侧判空问题
unity
牛哇网络工作室4 天前
UnityHDRP写实数字人全流程基础5—语音输入和语音识别
android·unity·c#·游戏引擎·aigc·语音识别·xcode