빌드 크기, 폰트, 글리프 아틀라스, 런타임 성능에 대한 명확한 답변 — 실제 개발자들의 질문에서 추린 내용입니다.
아니요. UniText는 폰트별 텍스처 아틀라스를 빌드에 굽지(bake) 않습니다. 글리프는 런타임에 공유 아틀라스(Texture2DArray 기반)로 래스터화되며, 글리프별 참조 카운팅과 LRU 방식 제거를 사용합니다. 빌드에는 폰트 데이터만 포함되며 — 아틀라스는 결코 포함되지 않습니다. 그 위에, 내장 Font Compression(Zstd, level 22)이 라틴/아랍어 폰트 데이터를 최대 ~2.7×까지 줄여줍니다(CJK는 더 완만한 ~1.3×).
아니요. UniTextFont 에셋은 폰트 바이트를 자체 내부에 포함합니다. 원본 폰트에 대한 참조는 에디터 전용이며(#if UNITY_EDITOR로 감싸져 있음) — 플레이어 빌드에는 아예 존재하지 않으므로, 런타임 종속성이 아니며 종속성 조회를 통해 AssetBundle로 끌려 들어가지 않습니다. 에셋이 생성된 후에는 .ttf를 삭제해도 됩니다.
아니요. UniText 도구 창을 통해 디스크의 어느 위치에 있는 .ttf에서든 UniTextFont를 빌드할 수 있습니다(파일 대화상자가 바이트를 직접 읽습니다). 폰트를 프로젝트에 임포트하는 것은 선택 사항입니다.
• WebGL: 빈 빌드는 ≈ 6–10 MB입니다(Unity 버전과 설치된 패키지에 따라 다름). UniText는 자체 네이티브 라이브러리(HarfBuzz, FreeType)를 추가합니다. 이는 Unity의 UI Toolkit Advanced Text Generator가 함께 제공하는 것과 동일한 업계 표준 라이브러리로 — 특별할 것이 없습니다(UniText는 단지 자체 사본을 포함할 뿐입니다). • Android: 두 아키텍처를 포함한 APK는 둘 다를 담습니다 — 아키텍처당 약 +4 MB. 하지만 AAB로 게시하면 Google Play가 기기별 번들을 전달하므로, 사용자는 둘 다가 아닌 자신의 아키텍처(~4 MB)만 다운로드합니다.
글리프는 세 가지 크기 — 64, 128, 256 — 의 타일에 담기며, 크기는 컨투어 복잡도에 따라 선택됩니다(세그먼트가 많으면 → 128/256, 컨투어가 단순하면 → 64). • SDF Detail은 복잡도 추정치에 대한 곱셈 계수입니다. 이 값을 높이면 이미 단순하지 않은 글리프가 더 큰 타일로 올라갈 가능성이 커집니다. (정말로 단순한 형태는 64에 머뭅니다 — 이 계수는 임계값을 넘는 글리프에만 영향을 줍니다.) • Tile Size Offset은 크기 사다리를 따라 이동하는 강제적인 단계입니다(선택된 64 + 오프셋 1 → 128; 범위 −2..+2).
글리프가 아직 아틀라스에 없을 때만 — 절대로 매 프레임마다는 아닙니다. “Hello world”의 경우 고유한 글리프 H e l o w r d를 래스터화합니다. 두 번째 H는 아무것도 하지 않으며, a는 그 a 하나만 래스터화합니다.
비용은 그 프레임에 래스터화되는 고유하고 처음 보는 글리프의 수에 비례하며 — 텍스트의 길이에는 비례하지 않습니다. 저희 벤치마크에서 첫 프레임의 ~2000개 고유 글리프는 일반적인 Android 기기에서 ≈ 1초이며, 동일한 시나리오에서 TMP와 UI Toolkit은 10+초가 걸립니다. 캐시된 글리프를 재사용하는 것은 비용이 없습니다.
UniText는 핫 패스에서 Unity Job System에 거의 의존하지 않습니다 — 의도된 설계입니다. 글리프 래스터화(저희 독자 기술인 lpSDF / lpMSDF)는 기본적으로 컴퓨트 셰이더를 통해 GPU에서 실행되며, Burst 잡은 컴퓨트를 사용할 수 없을 때의 CPU 폴백일 뿐입니다. 주요 멀티스레딩 — 셰이핑, 레이아웃, 메시 생성 — 은 UniText 자체의 OS 스레드 풀에서 실행됩니다(Unity Jobs가 아니며, 그래서 Jobs 프로파일러에 나타나지 않습니다). 따라서 “UniText 잡 없음”이 “스레드가 꺼져 있음”을 의미하지는 않습니다: 작업이 GPU로 갔거나, 아틀라스가 이미 예열되어 있거나, 텍스트가 병렬화 임계값 아래에 있는 것입니다(짧거나 단일 라벨은 순차적으로 처리됩니다 — 그렇게 하는 것이 더 저렴합니다).
“Update()는 느리다”는 릴리스 빌드에서는 통념일 뿐입니다. 네이티브 코드에서 매직 메서드를 호출하는 오버헤드는 실재하지만 무시할 만한 수준입니다(~1000회 호출 ≈ 0.16 ms) — 병목이 아닙니다. 순진하게 작성한 코드에서 진짜 비용은 콜백 자체가 아니라 AoS 메모리 레이아웃입니다. UniText는 중요한 곳에 SoA를 적용합니다(Unicode 속성, 메트릭, 픽셀별 버퍼). (ECS가 빠른 이유도 이것입니다 — “ECS 마법”이 아니라 SoA 덕분입니다.) 그리고 UniText는 어차피 중앙집중화되어 있습니다 — 단지 MonoBehaviour 매니저 형태가 아닐 뿐입니다: Update() 자체는 거의 비어 있고, 무거운 작업은 Canvas 렌더 콜백에서 단일 배치 패스로 모입니다(이것이 컴포넌트 간 병렬화를 제공하는 요소이기도 합니다).
저희 Discord에서 질문하십시오 — 바로 이런 질문들이 이 페이지를 만들었으며, 저희는 보통 빠르게 답변합니다.