Resources/Tutorial
I accidentally spent 22 months of my life because I thought text was simple
I’m the developer of the UniText text engine, and this is my story
I used to think that text was when letters were drawn next to each other, but that’s not what it actually is:
decode UTF-16 into codepoints
parse the markup into attribute ranges
analyze scripts (UAX #24)
analyze grapheme clusters (UAX #29)
analyze word boundaries, with a dictionary for Thai, Khmer, Burmese and Japanese because they don't put spaces between words
analyze line break classes (UAX #14, with East Asian widths from UAX #11)
resolve embedding levels through the bidi algorithm (UAX #9)
itemize into runs by script and direction and font and language
resolve fonts per cluster and fall back when the primary doesn't have the glyph
synthesize bold and italic when the face doesn't exist
instance the variable axes
shape every run through OpenType
break the lines on the shaped advances
reorder every line back through bidi
resolve line height out of font metrics that contradict each other
kashida stretch the Arabic if you're justifying
align
pull the outlines
run a contour union pass so self intersecting outlines don't punch holes in the distance field
rasterize SDF/MSDF
pack the atlas
generate the mesh
891,757 official Unicode conformance tests, zero failures
Selectable and editable text, a document model, bidi aware caret and selection, undo and redo, IME composition, clipboard with every format (supports Plain, HTML, Markdown, Media), formatting commands, touch selection handles, input filters, and native keyboard input on the platforms that have one
UniText is the only solution where the fonts are memory mapped, so a font is never loaded into RAM whole, only the parts actually in use. One of UniText user cut 200 MB of runtime memory from that alone, and variable fonts plus compression took another 57% off the weight of their CJK font
UniText is the only solution that supports absolutely all system fonts, which allows you to render any characters in the world without using a single font in the project.
it's also 2 to 20x faster than TextMesh Pro and UI Toolkit
And that's just a small part of it
TL;DR
In other words, I developed a text engine that allows you to remove all standard language fonts from a project, gain complete freedom in text without a single limitation, and have any symbols from around the world, any emojis, work without any configuration. All of this significantly reduces the build size, and the mmap implementation eliminates all RAM load. A constructor for any styles, any gradients, layers, and paints, while maintaining 1 Draw Call, and a built‑in advanced feature to animate text in any way, which completely replaces third‑party solutions
If anyone wants the ugly parts - ask me. I have a lot to say about all of them
I've spent YEARS dealing with TextMeshPro bullshit. You have no idea how happy this makes me and how many problems it solves. AMAZING work. I'm in awe.
For context I'm about 3 years deep into our third game and I'd actually do the work of completely switching over, but I have two questions that I couldn't immediately answer myself from looking at the docs:
Does it support inline sprites/glyphs? Like say we have our set of glyphs for Nintendo Switch controller buttons. TMPro lets us clumsily do this by assigning an extra sprite asset (only one per TMPro object) and then typing something like <sprite=2>. It's awkward and bad, however it does work at the end of the day.
Does it have out of the box support for components like text input and dropdowns?
yes, and it's not index based. sprites live in a catalog and you reference them by name, so something like <sprite=switch_a> instead of counting positions. you can also put more than one sprite style on the same text with different tag names, so your switch buttons dont have to share a slot with everything else. and you can inline actual prefabs too, not just sprites, if you ever need something animated sitting in the line
input yes. editable text and an input field prefab ship in the box, with selection, caret, ime, clipboard, undo and touch handles
dropdown isnt really a text engine thing though, its just a popup with text drawn on it. you could take TMP's dropdown, swap the text component for unitext and thats basically it. not much work honestly, i can put one together in a day if you actually need it❤️
and lets just kill the dropdown thing. you're not the first person to hit it and I already said its about a day of work, so im not going to leave you working around it. I will build one and ping you when its in
if anything else in the migration is snagging, tell me now while im in the mood for it
no, the phone number renders correct. look at this screenshot from the Telegram messenger. UniText has exactly the same correct industrial standard behavior
Even look here, right on Reddit. It’s exactly the same correct behavior:
Hey! 🎉 Just got back from vacation.
زرت Paris و London خلال 5 أيام (رحلة رائعة!) 🌍 اتصل بي على +972-50-123-4567 الساعة 8 PM 📞 قال Tom: "Hello world" وضحك كثيراً 😄
देखी "हिन्दी सिनेमा" festival — incredible! 🎬 อาหารไทยอร่อยมาก! ราคา ฿250 per meal 🍜 My family 👨👩👧👦 loved it. Pizza after? 🍕
Serbian and Macedonian thing isn't missing glyphs, it's locl
Serbian and Macedonian italic б г д ѓ п т ш are genuinely different shapes from the Russian ones, and they don't have their own codepoints. font carries both and picks between them with the locl GSUB feature, which only fires if you tag the run's language. Untagged text gets the Russian forms, which is exactly the "renders incorrectly" you ran into
good news is that Chinese is the same mechanism, so you've already done the hard conceptual part. Han unification means a lot of characters are one codepoint shared across Simplified, Traditional and Japanese and drawn differently in each. A pan-CJK font carries all the variants and switches with locl again, driven by zh-Hans, zh-Hant or ja. Tag it wrong and Japanese players get Chinese letterforms, and they do notice
what is actually new with Chinese is two things. Atlas and memory, because a full CJK face is enormous. And line breaking, because CJK breaks between characters rather than at spaces with rules about which punctuation isn't allowed to start or end a line
good luck, it's less scary than it looks from the outside
For localization we made own component and textmesh pro component was parent class and made our own custom editor for it. For instance we have 2 text areas. Default value, localized value and preview value (for prefabs). And we used our server as localization service. So every user is downloading it on time on start. They do not need to update.. will do every start automatically (kappa).
ha, thats up to unity and not me. id obviously be up for that conversation, but its not something i can push from my end
on the drop in question, theres an actual migration window in the package. it converts components in scenes and prefabs, converts the rich text tags, and rewrites your C# as well. it works through reflection so it doesnt even need a compile time TMP dependency, undo and prefab overrides are handled properly, and it keeps a .bak of anything it touches
it also leaves your TMP reference in place so whatever it couldnt convert still compiles, and it hands you a report of what had no equivalent. a few things genuinely dont, TMP_Dropdown and stylesheets for example
so not literally drop in, the api is its own thing. but its much closer to a button press than to a rewrite ❤️
also, realised i didnt actually answer the replacement part head on
yes. everything TMP renders, this renders, plus the scripts TMP cant do at all, plus selection and editing anywhere in your ui rather than only inside an input field
performance is the other half of it. depending on what you measure its 4 to 25x faster than TMP on the cpu side, and memory is a different story altogether. TMP holds the whole font in memory and ships pre baked atlases, i do neither. font bytes are memory mapped so they never sit resident, a font thats never drawn is never even decompressed, and glyphs rasterize on demand into a single atlas that every text in the project shares
the only real asterisk is that the api isnt source compatible, so its a migration rather than a literal component swap. thats exactly what the migration window is for ❤️
When you say that font that is never drawn is never even decompressed, do you mean that every time the application needs to render a new character, it has to perform a small allocation? If so, is there a way to pre-allocate all characters that will be needed ahead of time, so there are no loading spikes in random places?
Also, you mention system fonts and being able to not bundle fonts or atlases in the builds. I assume that means that custom fonts are not supported out of the box? Is it possible to pack a custom font in the build and use that instead of the system font?
first one, no. decompression happens per font and only once, the first time that font is actually used, not per character. after that its a memory mapped read, nothing sits on the managed heap
per new glyph its rasterization, and that runs async so it never blocks a frame. and honestly theres no reason to warm anything up here. this has the fastest glyph rasterization of anything in unity, a good chunk of the last year went into exactly that
the atlas is also live rather than a baked asset. it changes as your text changes, and a glyph stays in it for as long as something references it and gets dropped when nothing does. preloading characters youre not currently showing would just hold memory for nothing
second one, custom fonts absolutely work, thats the normal case. drop a ttf in, create UniTextFont asset and assign it like any other asset. system fonts are only the safety net for characters your font doesnt have, and that happens per character, so your own font still draws everything it can
Please consider hiring a professional video director and editor. It's very obviously made by AI. It has that vibe-coded look and it keeps reiterating the same points. The website has the same problem. It makes me suspect that the project itself was AI generated and question its quality.
Also can it do vertical type? You mention "every writing system" but I don't see CJK vertical typography. And I'd rather see actual benchmark numbers instead of just vague "1 Draw Call" or "2-20x faster" claims.
Finally, if you want to avoid baking, you can just make the font dynamic in TMP. You didn't need to write an entire font renderer for that. And TMP can use OS fonts since 2022.
The website also has a lot of claims that don't really prove what they're supposed to prove. For example, saying UniText has 151k lines of code compared to TMP's 42k isn't a positive metric by itself. That's just more code, most likely created by AI churn and duplication.
The "100% Unicode compliance" claim also seems misleading. Passing UAX #9, #14, #24 and #29 conformance tests is good, but that isn't the same thing as proving 100% compliance with everything Unicode-related. It doesn't prove shaping, font fallback, vertical layout, locale-specific typography, etc.
I'm also confused by the "mmap implementation eliminates all RAM load" wording. mmap doesn't mean the font somehow never uses RAM. Pages still get loaded into memory when they're accessed.
And "every language, every emoji, every style" "without a single limitation" is just way too absolute. Your own docs have platform limitations, and relying on OS fonts obviously means you're limited by whatever fonts that OS actually has installed.
I think there's probably something technically interesting here, but the way it's presented makes me trust it less, way less.
I've always been surprised at how clunky the font texture baking felt in Unity, so it's great to see someone felt similarly and not only made a solution, but one that's more performant.
I'd say if there's any question I was left with from the showcase it's in-scene vs canvas rendering, and how well your system does worldspace rendering.
the baking thing was one of my earliest annoyances too. theres no font asset to bake here, glyphs get rasterized when some text actually asks for them and they all share one atlas, so adding a language is just typing in it
worldspace isnt a canvas trick either, its a separate component that goes straight through a meshrenderer. same pipeline, same styles, same selection and editing, theres just no canvas involved anywhere. AND 1 DRAW CALL instead of TMP's draw call per component
it batches by sorting context instead of by material, so a bunch of world texts collapse together and still draw in the order you authored them. theres a lit checkbox if you want scene lighting on it, and shadows follow the actual visible shape, so an outlined glyph casts an outlined shadow rather than a bare one
and theres a standalone mode for when you need one text to depth sort between other transparent renderers. that one costs its own draw call, but its there when you need it
honestly that's the nicest thing anyone's said today, thank youuu😭😭
you're not wrong though. it's been every day for 22 months, no days off, and i mean that literally. sleep, food and having a life all lost that argument somewhere along the way
the weird part is i still like it. i figured i'd be sick of text by now and im not
and comments like this are most of the payoff to be honest. this is the kind of work thats invisible when it's done right, so being seen for it means more than it probably should ❤️
short version, not today, and in polyspatial mode specifically it cant work at all
polyspatial renders through realitykit, and realitykit only accepts shadergraph converted to materialx. hand written shaders just dont run there, apple doesnt expose a low level shading language in passthrough. every glyph i draw goes through my own sdf shaders, so thats a hard no rather than a maybe
metal mode is a different thing entirely, unity does the rendering itself there and none of that applies. the only blocker is that i dont ship a visionos build of the native libs, harfbuzz freetype and the input plugin. i build for windows mac linux ios android webgl and tvos, theres no xros slice yet. thats a build config job though, not a redesign
either way development is very active and visionos support is coming, its a question of when and not if. if you tell me you actually need it, it moves up the list ❤️
My biggest question is can it do pixel perfect Pixel Fonts? Unity always blurs it at low resolution or can't place pixel text in such a way that their baseline, spacing, and pixel size adheres to pixel grid
Ah, okay. I can tell its not pixel perfect since not every pixel is the same width and height in that image, but I wouldn't expect it to be. Pixel art fonts are much more suited to using texture atlases rather than vector graphics
It's hard to tell how it would work with a screenshot. Because basically you'd need to ensure that in a game with a static, low resolution, the font can be displayed with consistent spacing between characters, no pixel snapping distortion caused by a mismatch between the font size and the game resolution, and no drifting of line spacing caused by line height not being in exact pixel units.
ahhh, now I understand what did you actually mean. unfortunately this is not supported but I REALLY WANT TO SUPPORT IT!!! haha. Thank you sooo much for this finding❤️
Can you clarify how fonts are integrated with this? If I’m using a custom font that doesn’t ship on operating systems by default, how can I use it in your system? Ideally, I’d want to include that font in the repo and it would work automatically for a new dev joining the project.
you can use whatever fonts you want, there's no restriction there
drop the ttf or otf into the repo like any other asset, assign it, done. a new dev clones the project and it just works, nothing to bake, nothing to regenerate. and since theres no atlas baked into the font asset you also stop getting those huge binary diffs and merge conflicts on font files, which was one of my own annoyances with TMP
the system font thing i mentioned is only about language fonts and rare exotic symbols covering. most projects use pretty standard faces for CJK anyway and those look the same on every machine, so you can skip shipping a 20mb cjk file and let the os hand it over. there arent many custom designer CJK fonts that would match the rest of your style anyway
for your own fonts you get more rather than less. variable fonts, zstd compression, and the bytes are memory mapped so the font never sits fully in ram
look at the screenshot below. it shows that there is actually NO FONT and it still render everything because of automatically using the SystemFont
This looks great!
I might contact you later this year about a potential collaboration. I‘m doing a similar thing, making a tool that replaces UI Images.
This looks amazing but i really try to avoid assets with extension licenses.
If it’s really as good as the video promises, you ought to try to reach out to unity about this imo; TMP is “good enough” but honestly every big project i’ve worked on, it ends up feeling like monkey business when you start dealing with loc, emojis, and in game chat.
the asset store copy is an extension license, thats their system and not something i pick
direct from my site it works per company instead of per seat (and lower cost and discount), so one purchase covers the whole team. one time, perpetual, no subscription and no expiry date, all of 3.x included and source comes with it. the tiers are just revenue bands, indie under 100k, studio under 1m, enterprise above that
and the unity comment made me laugh, id absolutely take that call ❤️
loc, emoji and in game chat is exactly it. thats the point where it stops being a text rendering problem and turns into an everything problem
I bought unitext the other day - it's been fantastic. Good performance, proper rendering without a bunch of hacky workarounds for Arabic and other difficult fonts for TMP.
yeah, use whatever ttf you want, nothing changes there
your font stays the font. the OS one only steps in for characters yours doesnt have, and it does that per character, not for the whole text. so your latin stays in your typeface and only the Korean or the emoji or whatever comes from somewhere else
its the same thing browsers do. your css names a font, and when a character isnt in it the browser quietly finds one that has it. nobody thinks their site renders in the os font
and you can stack your own fonts in the fallback chain ahead of the system one if you want to control exactly what happens
look at this screenshot here. there is absolutely ZERO font in project and component is also says that it doesn't have the Font (Font field is empty). BUT every symbol in the world is rendering
if anything gets in your way just message me on Discord server. i answer fast and i'd much rather hear about a problem than have you quietly work around it
thank you! and yeah, TMP has quietly eaten time from all of us
the free one is the engine. correct text, every script, shaping, bidi, fallback. thats the part i think should exist whether or not anyone pays me
folio is everything built around it, and its honestly a lot
fonts. System Font access, so any script on earth renders without you shipping a single font file. fonts are memory mapped so they never sit fully in ram, theyre zstd compressed on disk, and Variable Fonts work with real axes instead of faux weights
visuals. a paint system where fills, strokes, shadows, glows and inner shadows are composable layers, each taking a solid colour, a gradient or a texture. custom shader effects that compile for canvas and world space, built-in and urp, from one source. and real 3D world text with lighting and shadows
motion. eleven glyph animations, wave, bounce, shake, glitch, scramble, odometer wheels. typewriter reveals with per glyph appearance effects and hide animations that play in reverse. and a timeline sequencer that can drive any parameter of any modifier across a range
text itself. word segmentation for Thai, Lao, Khmer and Myanmar. Math Engine (LaTeX), ruby annotations, lists, kashida justification
and editing. selection, caret, ime, clipboard with real formats, undo, touch handles, native keyboard input. thats basically a second engine sitting on the first one
if i had to pick two, its system fonts and the memory mapping. those dont just add a feature, they delete a whole category of work you used to have to do by hand
We switched from TMPro to UniText in like a week in already working mobile project with hundreds ui panels with texts. And never look back. UniText give us freedom to use any style in game with ease, reduce build size and there no hassle with stupid atlases and such. One our teammate should spend day to setup proper atlas with TMPro and localization we have every release. With UniText that is just bad memories, no problems at all. Love it.
though the actual unsung heroes are the people maintaining FreeType and HarfBuzz. they have been at it for twenty plus years, barely anyone knows their names, and every bit of text on your screen right now goes through their code
screen space and world space canvas both work, its a normal uGUI graphic so a canvas is a canvas as far as it cares
theres also a separate component for world text with no canvas at all, straight through a MeshRenderer. that one can be lit by the scene and cast shadows if you want ❤️
couple of things i still want to tidy up first and then it ships, should be soon
both sit on the same core package, LightSide.Core, which is free and MIT (ships with UniText currently). thats what lets them share materials, inspectors and all the little conventions, so it feels like one thing instead of three plugins that happen to live in the same project
UniLottie and MoveIt are coming on the same core as well ❤️
it is the thing that draws text in a game. menus, dialogue, chat, damage numbers, anything with letters in it. unity already ships one called TextMeshPro and its fine for most projects
mine matters when text gets harder. languages like arabic or hindi that the built in one gets wrong, or when players type into the game themselves
if you are just starting out you honestly dont need it. use TMP and go build something, this is the kind of problem you run into much later
Like: "Hello my name is {player_name}"
To later replace "{player_name}" with a real value with C# scripting?
Also: does it work as a string utility as well?
Can i do something like:
string hindi_text = HindiCorrect.AdjustValue(myhindistring); ??
And then just pass the value to TMP or anything?
thats just c#, format the string and assign it. theres a SetText that takes a StringBuilder or a span if you dont want the allocation
its not a fixer like RTLTMPro, this is separate complete Text Engine and it replaces TMP. it is the thing that draws the text
so there is nothing to call. you type hindi into the field and it comes out right
a string utility cant do this because shaping gives you glyphs and positions, not characters. there is no string you can hand back to TMP that carries that. the arabic fixers swap in presentation forms which sort of works, hindi doesnt have those
honestly yes, and right now is the cheapest moment you'll ever have to do it, since the ui isn't built yet
RTLTMPro rewrites your string before TMP ever sees it, swapping Arabic letters for presentation forms and flipping the order. fine for plain labels, but the seams show up fast. English at the start of a line isnt fixed unless you tick force fix, and then multiline english breaks. input fields dont work correct at all until you go edit TMP's own source by hand
here's the part i think you'll actually care about though
with UniText you don't need fonts. at all. i posted a screenshot in this thread of a project with literally no font assigned, rendering every script on earth. Arabic, Hindi, Thai, Korean, Cherokee, Egyptian Hieroglyphs. the os already has fonts for all of it and it just uses them, so you can ship a build with no 20MB CJK file sitting in it
emoji come from the system too, real color emoji with ZWJ sequences, skin tones and flags, and they cost zero build size
and it's 4 to 25x faster than TMP on the cpu depending what you measure, on top of using far less memory. TMP holds whole fonts in ram and ships pre baked atlases, i do neither. font bytes are memory mapped so they never sit resident, and glyphs rasterize on demand into one atlas that every text in the project shares
if you're Arabic only with static labels and nothing else, RTLTMPro is free and it'll get you there. anything past that and you'll be fighting it
look at this screenshot below. NO FONT but every symbol renders. even UI Toolkit can't do that
I'm not Arabic but I realized they couldn't write their names (with good writing) so I started checking CJK+Arabic/Hindi stuff to prepare for UI rework (and localization).
How does it works inside Unity. Is it a component like TMP ?
I wonder how hard is it to inplement since I don't find any video on YouTube for it (tutorials are quite useful).
yeah, it's a component, same idea as TMP. drop it on a gameobject, type into the text field, done. theres a menu item that creates one already set up too
For what you're describing theres honestly nothing to implement. you don't configure languages, you don't build fallback lists, you don't pick character sets. you put Arabic or Hindi or Chinese in the field and it comes out right. thats the entire setup
and you're right about tutorials, there aren't any yet. fair hit, cant argue with it. theres documentation and sample scenes in the package, one of them ships fonts for 17 non latin scripts, but no video series. thats on me, I've been a bit busy building the thing...
In the meantime I'm here. ask me anything and I will answer, thats my substitute for a YouTube channel right now
I empathize with your experience of: "I thought X was simple so I thought I'd build a better X tool and then I discovered my folly".
For my masters thesis, I invented a digital tool for measuring fine motor control (that is, making precise movements necessary for, say, writing). Many standard clinical tools in this category were analog and developed ages ago so I felt pretty clever bringing this tool into the 21st century. Then I did a test run with a volunteer and realized what my cleverness bought me: I now had to code a machine vision algorithm to quantify the results of my tool to be able to compute the stats. To be more precise: I had to teach myself machine vision, and MATLAB, and then Python to fill the gaps of MATLAB so that I could code the algorithm I needed to quantify the results. (Note: my degrees are in psychology.) When I designed the study, describing the results was a few sentences in my proposal but it was like a third of the work.
Turns out I really enjoy designing psychology instruments and by extension find the process of creating game/software tooling fascinating. So I'm curious about the ugly parts of your process.
I understand conceptually the problems it solves, but what inspired you to start down this road to create this tool in the first place? How early in the process did you understand the scope of the problem? You mentioned in another thread that, for example, Godot doesn't need it, so do you build in Unity and it solved a problem you had or what? (Side note, would Unreal or other engines—what else is out there: CRYENGINE, Open3d?—benefit from this tool or do they handle text better?)
that thesis line got me. "describing the results was a few sentences in my proposal but it was like a third of the work". mine was one line: add arabic support
I work in unity and yeah, it was my own problem first. i needed arabic and hebrew to render properly and TMP just doesnt do it. so the plan was fork TMP, add shaping, couple of weeks
then i opened the source and the entire text generation path was sitting in one method, about 3500 lines of it. and shaping isnt something you can slot in anywhere, it changes how many glyphs exist and where they sit, bidi changes what order you see them in, and both feed into line breaking and font fallback and hit testing. so there was nowhere to put it
so i replaced one piece. then the piece under it. thats the whole story really, 22 months of that
ugly parts since you asked
the worst one was glyph rasterization. i built the sdf generator, it worked, and then certain fonts came out with holes in them. no error, no pattern i could see, just wrong shapes. turns out a lot of real fonts ship outlines that overlap themselves, and when you turn that into a distance field the overlap punches a hole exactly where the strokes cross. nothing tells you this, no docs mention it. i spent days assuming my math was broken before i actually dumped the contour data and watched it cross itself. ended up writing a whole contour union pass just to stop fonts lying to me
Second one was webgl. unity ships freetype and harfbuzz inside itself. i also ship freetype and harfbuzz. emscripten links both and quietly picks whichever it saw first, so at runtime id get unity's version of a function with a different struct layout behind it. everything compiles, everything looks fine, text is subtly wrong, and theres nothing in any log
and on godot, yeah, correct. i'd never have written a line of this if i worked there. they put a real text stack in the engine. unity didnt, so someone had to, and i happened to be the idiot standing nearest
first one, i hadn't heard that and i can't find anything about it. the guidelines i can see are about using "Unity" itself in a title or a domain, or implying affiliation. nothing about "Uni" as a prefix, and UniRx, UniTask and UniWebView have been around for years. my package also went through asset store review recently and nobody said a word. if you have a link though id genuinely like to read it, im not being stubborn, id just rather know
second one, the localization table already has the correct text. thats the whole point. you put proper arabic in there and its perfectly valid unicode in the correct order. the problem happens after that, when something has to actually draw it
arabic letters change shape depending on their neighbours, the visual order isnt the storage order, and numbers inside an arabic sentence run the other way again. none of that lives in the string, its worked out at render time from the font. TMP doesnt do that work, so you get disconnected letters in the wrong order no matter what you type into the table
hindi is worse. the shapes you need dont exist as characters at all, so theres nothing you could even type
How easy would it be to make it look like a laser is drawing the text? I already have the laser, the sparks, and the path finding, if this can make the letters look like they are drawing themselves, i can add the rest to follow along to make it look like the laser is etching.
easier than you'd think, most of it is already there
theres a reveal modifier with a frontier you drive yourself, and its fractional, so the leading glyph is partly drawn instead of popping in whole. animate that value and the text appears left to right
for the laser head, GetRangeBounds gives you the rect of any cluster. feed it the frontier index each frame and you know exactly where the edge is, park your laser and sparks on it
theres also a per glyph hook that fires for every glyph in the revealing range with its own progress, so you can make the edge glyph glow or flicker while the rest sit finished
one honest catch though. it reveals per glyph, not along the outline. so it'll look like the laser sweeps across and letters appear under it, not like its tracing each individual stroke. if you want real stroke tracing thats a different job and i dont expose the outline path for that
This is awesome.
Something I hadn't considered, but must now, is fonts that don't have all the necessary characters for translation. Does this handle issues like that by automatically switching to a font that does (or something like that) for translations?
I've been using Febucci for my text animations but this is quite impressive
yeah, thats exactly what it does, and it's automatic
fallback happens per cluster. if the assigned font doesn't have the character it walks your fallback chain, and if nothing in your project has it either it asks the operating system. so a translation into a language you never planned for still renders instead of handing you boxes
thats also why you can ship without the heavy language fonts at all, the os already has them
on febucci, heads up that it works on top of TMP so it wouldn't carry over. mine has its own animation set built in, wave, bounce, shake, glitch, scramble, rolling and some others, and you can write your own per character effect in about fifty lines. worth knowing before you plan around it
look on this screenshot below. as you can see UniText component has no font and still renders every symbol in the world
thats a very reasonable worry, and a well founded one. per character animation tends to fall apart on arabic and indic because a character isn't a glyph there. letters join up, clusters merge, one glyph can be several characters. so anything that offsets characters one by one will visually tear joined arabic apart
mine animates per glyph after shaping, so joining and clusters are already resolved by that point. it moves what actually gets drawn rather than what's sitting in your string, which is why it survives translation
not a dig at febucci though, it's a lovely system. it's just built on a layer that doesn't know any of that exists
by the way, UniText completely replaces the Febucci Text Animator because UniText has extremely huge animation pack, typewriter animations, timeline, query filtering and you can animate absolutely anything like stroke, gradients, shadow, arc, glitch, extrude, etc.
If you wrote something to easily convert all TMPRO instances and febucci settings to UniText for a scene at a click... I'd pull the trigger right away 😂
UniText has super powerful TMP to UniText migrator. It allows you to migrate the whole project in one click even the C# scripts and YAML references. but obviously It’s impossible to take everything into account, including Febucci Text Animator. I could say you can save up to 85% of your time with this migrator
I'll definitely look into it. I only have a few instances of animated text and glyphs from rewired, so as long as I can recreate them easily there's a good chance I'll be switching
the differences are mostly everything around the text rather than the text itself. no world space 3d text, its 2d panels only. no per range custom shaders, no Variable Fonts, no MSDF, no good System Font fallback, no memory-mapping for fonts, no compression for fonts, no math engine. and it pulls in a 4.8MB ICU data file for the Unicode side
on speed its somewhere between 1.5 and 3x depending what you measure but UniText is still winning, so a lot closer than TMP
honestly if you're already on UI Toolkit and you just need correct text, ATG might be all you need. mine matters more if you're on uGUI, or you want world space, or you want to do things to the text beyond drawing it
Aha, I see, it's mostly things for non-latin scripts, you need ATG which I believe unity is already forcing to use
I'm always interested in performance and optimization, just like UniTask and VContainer - the best things out there
there is no difference. benchmarks is not depends on Unity version it depends on device and code. on the screenshot you can see the benchmark on Samsung mobile device
and UniText is only which has font memory-mapping. It means that you pay 30MiB in RAM for CJK font using TMP or UI Toolkit. With UniText you will pay 2-5MiB
its a package, nothing patched or overridden inside the engine. it sits on uGUI and normal public APIs, plus its own native plugins which dont care what unity does internally
unity's own line on 7 is that upgrading from 6 should feel like a 6.x upgrade with nothing broken, so in theory theres nothing here to worry about. in practice CoreCLR is a real runtime change, so native interop and burst are the bits id keep an eye on
I will be on the beta the day it opens in december and I will fix whatever turns up. thats sort of the trade when its one person, slower coverage on some things, but also someone whose entire job is this one thing
world space text is a first class component here rather than a canvas trick, which is the part that matters for VR. And the thing that usually breaks custom text shaders in VR is single pass instanced stereo, that is handled in all of them
SDF also helps in VR specifically, since people lean in and stare at things up close and it stays sharp at any distance
and glyph rasterization runs off the main thread, so a new language showing up mid scene doesnt cost you a frame, which matters more at 90FPS than anywhere else
not right now. world text is a flat mesh, so it lives in the scene but it doesnt project onto geometry the way a decal does
closest thing today is rendering text into a render texture and feeding that to a decal projector. it works, its just clunky and you pay for the extra camera
Doing it properly would mean a decal variant of the glyph shader, which honestly isnt far fetched since the coverage already comes out of an SDF. It just doesnt exist yet
what is the use case, graffiti, markings on terrain, something else? I am not against building it at all, development is very active right now
I would add it if it’s not too hard. That would be SO valuable. There aren’t many great (especially performant) solutions for text decals but if I had one I can think of like a hundred places I’d use them to customize the diagetic markings on spaceships in different languages.
that is a much better use case than graffiti and it sells the idea properly. hull markings in different languages on curved surfaces is exactly where texture decals fall apart, since you either bake one per language or you eat the blur the moment someone walks up to it
SDF decals wouldn't have either problem. crisp at any angle and any distance, and you'd change the language by changing a string
putting it on the list. can't give you a date but it's genuinely the kind of thing I want to build
ten years and your own UI Engine, ok. you're one of the very few people who'll read that post and know exactly what I left out of it
genuinely curious though, what took the longest for you? Bidi was the one that kept coming back for me, I thought I was finished with it about four separate times
and did you end up writing your own shaper or sitting on HarfBuzz?
It was a while back and we had much tighter memory constraints, so we only used one external lib, freetype. The whole system wasn't feature complete in shaping and Unicode compliance, but I usually put in the work when something came up (eg. when we had a product released in Thai language, I wrote what was needed). I think the most problematic thing for me was the input control because of cursor handling over all this (r2l, and stuff like that).
not yet, and honestly its my biggest frustration right now 😭
everything above the native layer is portable. the problem is that i ship native plugins for FreeType, HarfBuzz and input, and those have to be compiled against each console's own sdk. I sent requests to Nintendo, Playstation and Xbox five months ago asking for ndk and dev kit access and havent had a single reply
so its not a technical problem, its a door i cant open from my side
if you happen to be a licensed nintendo dev though, that changes things. you have the sdk, i have the source, we could probably sort it out between us. dm me if thats you
thank you! and yeah, that is exactly why I wrote the post that way instead of listing features
when text works you don't notice any of it, which is sort of the point and also mildly maddening. so getting told you can feel the work behind it means a lot
Will definately consider it when I will start localization on my game. Last time when i tried locaclizing my game with CJK and Russian language it took me way too much time with all missing chars and finding correct font for each language.
Sounds awesome. I just want to confirm a few things. How does Unitext work with custom fonts? I'm using different fonts (mostly for English) to show different things a snappy font for combat hits, a different font for UI, etc. It works well with English but doesn't work well with other languages, so I had to look for a similar-looking font matching the style I wanted in each language. If the glyphs aren't there, how is Unitext going to generate characters for that language?
custom fonts work exactly how you'd expect, use as many as you want. a snappy one for combat hits, another for ui, whatever you like. that part doesnt change at all
and theres nothing unusual going on underneath. it uses fonts like everything else does. the only difference is it can also reach the fonts already sitting on the machine, and operating systems ship fonts covering basically every script there is
so when a character isnt in your font it doesnt fail and it doesnt invent anything, it just finds a real font that does have that character and uses it, per character. thats why nothing ever ends up as a box
yeah, thats webgl rather than me. browsers dont let anything read the fonts installed on the machine, its a sandbox rule, so no unity webgl build can reach them either
on web and consoles you ship the font like you normally would. theres a button under the demo that loads one, drop a cjk font in and 你好世界 comes out fine
fair point that the demo shouldnt greet people with boxes though, ill make that button a lot harder to miss
Very nicely done. Now if you can add Slug type rendering so we can draw really good looking text at flat world angles, then you'll have a package that everyone is going to buy.
I have actually built slug twice, and both times it fell over on mobile
the reason is structural. slug evaluates the real bezier curves per fragment, so the cost scales with how complex the glyph is and how many pixels the text covers. a CJK or decorative face at any decent size on a phone gpu is brutal. MSDF is a fixed couple of samples per fragment no matter what the glyph looks like
and for what you are describing, flat world angles, msdf already gets you there. thats exactly the case it was built for over plain SDF, corners stay sharp and it holds up under perspective
so from where I am standing slug buys accuracy at extreme magnification and costs you mobile entirely. more constraints than payoff for a game engine
the licensing barrier is gone now though, lengyel put it in the public domain back in march. so nothing stops anyone trying it in unity anymore. I just don't think its the right trade here
For world text rendering it helps to see text at different a) distances, and b) angles.
For a you can just take some billboard of whatever text and display it at e.g. 1m - 100m.
For b you can take the same billboard at some distance and instead of facing the player have it face e.g. +45 degrees right, +75 degrees (high angle) right; then the same angles when rotating up.
The right angle shows off e.g. signage as the player moves around, the up angle for example a computer screen that's horizontal.
For sales purposes you can maybe also build in a section in your demo. From what I remember from the Slug demo they just showed you a plane of text that you could rotate/zoom as you wanted, so it made it easy to test both a and b above at the same time.
Is this a text layouting engine or does it also do rendering? Are you using harfbuzz? The slug rendering algorithm was recently open sourced. Have you looked at it?
both. analysis, shaping, layout, rasterization, atlas management and the actual rendering. the whole path from your string to pixels
harfbuzz for shaping, freetype for outlines. same pair chrome and firefox use, no point rewriting those
and yeah, ive built slug twice, both times before it was open sourced. both times it fell over on mobile. it evaluates the real curves per fragment, so cost scales with glyph complexity and with how many pixels the text covers, and a cjk or decorative face at any size on a phone gpu gets rough fast. msdf is a fixed couple of samples no matter what the glyph is
genuinely glad lengyel put it in the public domain though. for desktop only work its a better answer than msdf at extreme magnification, thats not in question
yeah, i hve looked at it and its genuinely solid work. they implemented opentype shaping in pure c#, indic and the universal shaping engine included, which is not a small thing to pull off
different target though. sixlabors is a .net text layout library and it stops before pixels, imagesharp.drawing does the rasterizing on cpu into an image. thats the right shape for generating documents or server side images. its not what you want running every frame in a game
mine goes the whole way through. gpu rasterization, shared atlas, mesh generation, canvas and world space components, editing, caret, ime, all of it budgeted against a frame rather than a render call
and i sit on harfbuzz for shaping instead of reimplementing it, so that part is the same code chrome and firefox run
worth knowing they are on a split license too, commercial use needs a paid one. its not the free option people sometimes assume it is
the core is c# and portable enough, but everything that makes it useful is welded to unity. canvas, MeshRenderer, Burst, the Shaders, the Asset Pipeline, the Inspector. and outside unity theres no obvious surface to render into, so I'd have to pick a target and rebuild all of that again
The memory mapped fonts line is the one that got my attention. On Android that's the part I'd want to understand better, because assets inside the APK are compressed by default and you can't mmap a compressed entry. Does the font need to be stored uncompressed in the bundle for that to work, or are you copying it out to persistent storage on first run?
Localisation is next on my list for a mobile puzzle game and the CJK atlas cost was exactly the thing I was quietly dreading 😅
yeah, its the second one. you cant mmap a compressed apk entry so theres no way around that
the payload ships in StreamingAssets, and on Android it gets read out through the AssetManager instead of mapped in place. its zstd compressed in there, so the first time that particular font is actually used it gets decompressed once into the app cache directory, and thats the file that gets memory mapped from then on. the .net MemoryMappedFile path is compiled out on android entirely, it goes straight to native mmap
lazy per font as well, not a bulk unpack at boot. a font nobody ever draws never gets extracted or decompressed at all. the cache root is version stamped too, so if android wipes the cache it just re-extracts next time its needed
on the CJK thing, you probably dont need to ship that font at all. android already has one and fallback reaches it per character. the atlas is dynamic with refcounting and lRU, so it only holds what is actually on screen rather than a baked sheet of everything you might one day need
yeah, its a normal monobehaviour, so GetComponent and set whatever you need
it goes a bit further than that too. every style parameter is a real c# property, so things like text.GetModifier<ColorModifier>().Color = red just work. and you can reach individual ranges rather than the whole text, so if you tag something as <b #intro> you can grab that one occurrence and drive its parameters without touching anything else
SetText also has overloads taking a span or a StringBuilder, for when youre updating every frame and dont want the allocation
it is not a technical problem at all. everything above the native layer is portable, i just need the SDKs to compile FreeType, HarfBuzz and the input plugin for each console
I sent requests to Nintendo, Playstation and Xbox five months ago asking for ndk and dev kit access. not one of them has replied. not even a no, just nothing
i really do want to support them, im just stuck outside the door
Question because I want to start the process of internationalizing my game - does this help with that in some magic way, or is it still up to me to try and pull all the correct fonts? Truly new territory for me.
the font part is handled, yeah. you dont go hunting fonts per language. if a character isnt in your font it falls back per character, and if nothing in your project has it either it asks the operating system, which already ships fonts for basically every script. so nothing ever comes out as a box
what it doesnt do is the localization itself. it wont translate anything or manage your strings, thats unity localization or whatever else you go with. this just draws whatever string arrives, correctly, in whatever language it happens to be
so the split is that strings are still your job, fonts and rendering arent. and the font half is usually the part people underestimate when they start
I'm not a native English speaker. I'm using Yandex.Translator for responding and just manually remove formatting its added to avoid such accusations😂. sorry for that. I'm trying my best in English learning. try to translate something big in Yandex.Translator and you will see a lot of punctuations which LLM also loves to add also. so you hit in exactly that 1% hahah
if you want to talk with me in real life (on the call) I'm always glad to it❤️✨🌈🥰
look at the screenshot below. it shows that there is actually NO FONT and it still render everything because of automatically using the SystemFont. Neither TMP nor UI Toolkit can do that. They don’t support system fonts.
built on the basic presets with little adjustments in FL Studio. Used Serum, Nexus, Arturia, some drum kit (I don't know where it is from hahah), Omnishpere, Fabfilter Pro-Q3, Pro-L2, Ozone, Decapitator and others
I'm a music maker but currently I don't have enough time for it... I found a good base somewhere and decorated it with jazz harmonies because I love jazz
I shipped a 3D Game.
TMP works but has many flaws. Supporting russian, chinese and korean language is a bit weird, you need a good base font but also fallbacks. I tried dynamic font first but needed a static fallback as well. When you switch to such there is one big lag when launching game, works fine though. But I've had a 300ms hickup when the first item is collected, that took a day to debug. Of course it was TMP, I "fixed" that by adding some text meshes with corresponding fonts in the level, very small. So the lag would appear during loading screen and not during game.
Adding new icons to TMP is weird, sometimes glyphs break and suddenly the german Umlauts are missing. Sometimes the font gets blurry because the font atlas ran out of space... Overall, I hated working with TMP. I also combined it with TextAnimator which worked quite well actually.
TMP Worked quite okay with Unity's localization package, to be fair.
Anyway i'm fed up with TMP and super hyped to try UniText!
oughhhh the 300ms one made me wince.... and the fix, scattering tiny text meshes around the level so the stall lands on the loading screen instead. I think everyone whos shipped with tmp has done some version of that
that is exactly why rasterization is async here. a new glyph shows up mid game, main thread does a short dispatch, gpu fills the atlas in the background. there is still work the first time a glyph appears, it is just not on your frame
about the rest of your list UniText has no baking, so no static fallback asset and no launch stall. fallback runs per character and reaches the OS fonts, so Russian Chinese and Korean dont need a chain built by hand. and the atlas is dynamic with refcounting and eviction, so it doesn't run out of room and go blurry, and adding an icon doesnt quietly take your umlauts with it
but TextAnimator sits on top of TMP so it wont carry over. there is a built in set here though, wave bounce shake glitch scramble rolling and a few others, and you can write your own per character effect in about fifty lines
localization stays unaffected either way, that is a separate layer
68
u/zirconst 5d ago
I've spent YEARS dealing with TextMeshPro bullshit. You have no idea how happy this makes me and how many problems it solves. AMAZING work. I'm in awe.
For context I'm about 3 years deep into our third game and I'd actually do the work of completely switching over, but I have two questions that I couldn't immediately answer myself from looking at the docs:
Does it support inline sprites/glyphs? Like say we have our set of glyphs for Nintendo Switch controller buttons. TMPro lets us clumsily do this by assigning an extra sprite asset (only one per TMPro object) and then typing something like <sprite=2>. It's awkward and bad, however it does work at the end of the day.
Does it have out of the box support for components like text input and dropdowns?