Шрифты: загрузка, размер, перенос и fallback
Полный пример C#: встроенный шрифт, FontFile, SystemFont и FontVariation; настройки качества и экспорта.
Запустить пример без внешнего файла
Добавьте UiSampleStyle.cs и FontsDemo.cs в C#-проект. Прикрепите FontsDemo к корню Control отдельного уровня, соберите и запустите F6.
Две надписи показывают латиницу, кириллицу и комбинируемый акцент. У них разные размеры; большая использует FontVariation с синтетическим утолщением. Длинная нижняя надпись переносится при сужении окна. Многоязычная строка специально одинакова в обеих версиях примера.
FontsDemo.cs
using Reckfell;
public partial class FontsDemo : Control
{
public Label Small { get; private set; } = null!;
public Label Large { get; private set; } = null!;
public FontVariation Bold { get; private set; } = null!;
public Label Wrapped { get; private set; } = null!;
public override void _Ready()
{
UiSampleStyle.Prepare(this);
var margin = new MarginContainer(); AddChild(margin); margin.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect, margin: 24);
var column = new VBoxContainer(); margin.AddChild(column);
Small = new Label { Text = "Reckfell — Привет! Cafe\u0301", AutowrapMode = TextServer.AutowrapMode.WordSmart };
Large = new Label { Text = Small.Text, AutowrapMode = TextServer.AutowrapMode.WordSmart };
column.AddChild(Small); column.AddChild(Large);
Small.AddThemeFontSizeOverride("font_size", 16); Large.AddThemeFontSizeOverride("font_size", 28);
Wrapped = new Label
{
Text = UiSampleStyle.Text("Длинная надпись переносится в пределах доступной ширины. Размер шрифта задаётся отдельно от файла.", "A longer label wraps within the available width. Font size is configured separately from the font file."),
AutowrapMode = TextServer.AutowrapMode.WordSmart
};
column.AddChild(Wrapped); SetFont(Small.GetThemeFont("font"));
}
private void SetFont(Font font)
{
Theme.DefaultFont = font;
Bold = new FontVariation { BaseFont = font, VariationEmbolden = 1.2f };
Large.AddThemeFontOverride("font", Bold);
}
public Error LoadProjectFont(string path)
{
if (!path.StartsWith("asset://") || !FileAccess.FileExists(path)) return Error.FileNotFound;
var font = ResourceLoader.Load<FontFile>(path);
if (font is null) return Error.FileUnrecognized;
SetFont(font); return Error.Ok;
}
public Error LoadExternalFont(string path)
{
const ulong maxBytes = 16UL * 1024 * 1024;
if (!FileAccess.FileExists(path)) return Error.FileNotFound;
byte[] data;
using (var file = FileAccess.Open(path, FileAccess.ModeFlags.Read))
{
if (file is null) return FileAccess.GetOpenError();
ulong length = file.GetLength();
if (length == 0 || length > maxBytes) return Error.InvalidData;
data = file.GetBuffer((long)length);
if ((ulong)data.LongLength != length) return Error.InvalidData;
}
var font = new FontFile(); font.SetData(data);
SetFont(font); return Error.Ok;
}
public void UseSystemFont(string[] names) => SetFont(new SystemFont { FontNames = names });
}Загрузить свой шрифт
| Источник | Действие |
|---|---|
| Шрифт проекта | Добавьте TTF/OTF в проект, дождитесь импорта, вызовите LoadProjectFont с asset://путём |
| Внешний динамический файл во время работы | LoadExternalFont ограничивает чтение 16 МиБ, проверяет число байтов и назначает их через FontFile.SetData |
| Семейство, установленное в ОС | UseSystemFont с массивом имён; наличие и результат различаются на устройствах |
| Bitmap-шрифт | Импорт BMFont .fnt или импорт изображения шрифта; настройте сетку/набор символов под источник |
Размер задаётся у Control через Theme Overrides → Font Sizes или Theme.SetFontSize, а не отдельной копией TTF на каждый размер. Поля Fonts и Default Font выбирают ресурс. В этой сборке встроенный резервный шрифт — Open Sans SemiBold; проектная тема/шрифт могут его заменить. Для воспроизводимого экспорта добавляйте нужные файлы и fallback-ресурсы в проект: SystemFont не упаковывает установленные шрифты ОС в игру.
Внешний загрузчик выше отклоняет отсутствующий, пустой, слишком большой или недочитанный файл до замены шрифта. 16 МиБ — лимит примера, а не ограничение формата движка. Error.Ok означает принятие байтов: это не валидатор произвольных шрифтов. Проверьте формат, нужные символы и результат отображения; не используйте пример для непроверенных загрузок пользователей без отдельной проверки входных данных.
LoadDynamicFont — другой путь: он сам читает файл и сбрасывает настройки, но в 1.0.0 возвращает Error.Ok без проверки декодирования. Одного этого результата недостаточно. В примере чтение ограничено заранее; SetData нового ресурса не вызывает такой сброс.
Покрытие символов и shaping
Отсутствующий глиф требует подходящего fallback. Назначьте Font.Fallbacks в нужном порядке и проверяйте реальные строки языка, включая знаки, диакритику и эмодзи. HasChar полезен для одного символа, но не доказывает корректность всей письменности, лигатуры или последовательности emoji. Системный fallback зависит от устройства. Для icon font храните стабильное соответствие кодовой точки и значка, проверяйте версию файла.
Длина .NET string измеряется UTF-16 единицами; она не равна числу видимых графем. RTL, комбинируемые символы, лигатуры и IME требуют TextServer/shaping, а не ручной расстановки букв. Сначала разрешите перенос и выделите ширину, затем проверяйте выравнивание и TextDirection. Scale всего Control не заменяет размер шрифта.
Качество и специальные возможности шрифта
| Настройка | Когда полезна / ограничение |
|---|---|
| Antialiasing | Выбор сглаживания; для пиксельного шрифта проверьте режим без сглаживания и целые координаты |
| Hinting | Подгонка динамического шрифта к пиксельной сетке, особенно на малых размерах |
| SubpixelPositioning | Точность положения; влияет на кеш и характер движения мелкого текста |
| Mipmaps | Помогают при уменьшении, требуют памяти; не лечат низкое исходное разрешение bitmap |
| MSDF, MsdfSize, MsdfPixelRange | Масштабирование и контуры на расстояниях, поддерживаемых выбранными параметрами; проверьте мелкие размеры и артефакты |
| Outline/shadow у Control | Цвет, размер контура и смещение тени задаются элементами темы; оставьте место до границ и clipping |
| Variable font | FontVariation.VariationOpentype задаёт поддержанные оси. Используйте числовой ключ TextServerManager.GetPrimaryInterface().NameToTag("wght"); диапазоны берите из GetSupportedVariationList. Строка "wght" в 1.0.0 может не изменить выбранный кэш |
| Faux bold/italic | VariationEmbolden и VariationTransform синтезируют начертание; это не замена отдельного качественного bold/italic файла |
| Spacing / baseline | FontVariation меняет интервалы и смещение; повторно проверьте строки и высоту элементов |
| OpenType features | Используйте поддержанные файлом теги через OpentypeFeatures; лигатуры/цифры зависят от шрифта. Это отдельная настройка от осей FontVariation.VariationOpentype |
| Цветные emoji | Нужны покрытие и поддерживаемый цветной формат; проверьте текст на целевой платформе |
| Prerendering | Настройки импорта могут заранее создать нужные глифы/размеры; это обмен времени первого показа на размер и память |
Форматы динамического импорта включают TTF/OTF/WOFF/WOFF2; свойства доступны в FontFile и расширенных параметрах импорта. Для bitmap качество ограничено исходными изображениями и выбранным масштабом. Не включайте все варианты размеров и осей заранее без оценки памяти.
У нового FontFile MsdfPixelRange = 16, MsdfSize = 48, SubpixelPositioning = Auto. После LoadDynamicFont значения сбрасываются на 14, 128 и Disabled соответственно. Параметры качества задавайте после загрузки. MSDF не поддерживает hinting; Oversampling обходится для MSDF и положительного FixedSize. Точные настройки FontFile.
Настройки проекта и диагностика
Общий ресурс задаёт gui/theme/custom_font; качество встроенного шрифта — gui/theme/default_font_*, масштаб темы — gui/theme/default_theme_scale. Учитывайте требования перезапуска и переимпорта. Проверенные ошибки чтения в примере сохраняют прежний шрифт; содержимое успешно прочитанного файла требует отдельной проверки.
Если видны квадраты — проверьте покрытие и fallback. Если текст обрезан — ширину, перенос, минимальные размеры, clipping и контур. Если после экспорта другой вид — наличие ресурсов и использование SystemFont. Если первый показ тормозит — набор кешируемых глифов/размеров и импорт, а не только FPS игры. Тест этой статьи использует приватный TTF Windows для проверки загрузчиков; файл шрифта не распространяется с примером.