FOnline Engine
Current master GitHub
Документация Docs/ru/how-to/content/image-format.md

Форматы изображений и спрайтов

FOnline запекает авторские и устаревшие изображения в компактный клиентский контейнер спрайта. Baker импортирует статические RGBA-источники, устаревшие индексированные форматы, анимации, наборы направлений и текстовые композиции .fofrm. Он также может заменить обычный полноразмерный quad проверенным индексированным силуэтом и обрезать сериализованный RGBA-холст по этой геометрии. В runtime штатный клиент читает версионированный контейнер, размещает конкретные кадры в атласе текстур и создаёт AtlasSprite либо SpriteSheet.

Используйте это руководство для решений по авторингу и рабочему поведению. Точный контракт текущей ревизии находится в сгенерированном справочнике image-format, его страницах исходных форматов, FOFRM, параметров имени файла, запекания, runtime и проверки, а также в канонической JSON-модели.

Область действия и источник истины

Поведение принадлежит следующим исходникам:

  • Source/Tools/ImageBaker.cpp и ImageBaker.h: обнаружение источников, декодирование форматов, композиция FOFRM, имена выходов, SpriteInfo каждого пакета, интеграция mesh и сериализация;
  • Source/Tools/SpriteMeshing.cpp и SpriteMeshing.h: построение маски, генерация и оценка кандидатов, триангуляция и проверка покрытия;
  • Source/Common/SpriteResource.cpp и SpriteResource.h: общий версионированный декодер, записи кадров/mesh и формат индекса SpriteInfo;
  • Source/Client/DefaultSprites.cpp и DefaultSprites.h: загрузка запечённого контейнера, AtlasSprite, SpriteSheet, смещения кадров и загрузка в атлас;
  • Source/Client/SpriteManager.cpp: диспетчеризация по расширению и кэши спрайтов;
  • Source/Client/TextureAtlas.cpp: выделение места в атласе;
  • Source/Tests/Test_ImageBaker.cpp и Test_TextureAtlas.cpp: исполняемые примеры импорта, отказов и выделения памяти.

BuildTools/ImageFormatInterface.json является структурированным контрактом, подтверждённым исходниками. BuildTools/docs_image_format.py проверяет каждый объявленный source anchor, выводит актуальные списки расширений baker и runtime, подтверждает, что устаревшее поле FOFRM Effect по-прежнему отсутствует в сериализованном результате, и строит сгенерированный справочник. Изменение импортера, descriptor, контейнера, factory, atlas или cache должно обновлять эту модель в той же правке.

Эта страница описывает переиспользуемый Engine. Встраиваемый проект владеет своим каталогом ресурсов, лицензиями, приоритетом resource pack, политикой исходных файлов, визуальным стилем, подстановками анимаций, ожиданиями hit-test, настройкой движения и эталонами визуальной приёмки.

Выбор исходного формата

Задача Предпочтительный источник Примечания
Обычное статическое изображение или прозрачный спрайт PNG Рекомендуемый lossless-вход для авторских ресурсов проекта. Палитра, grayscale малой разрядности, tRNS и 16-битные каналы нормализуются в RGBA8.
Существующий выход TrueColor-конвейера TGA Используйте поддерживаемое подмножество: type 2/type 10, 24/32 bpp, без image ID, начало координат снизу слева.
Многокадровый или направленный спрайт проекта FOFRM со ссылками на PNG/TGA Композиция, смещения, дельты кадров и timing остаются явными и удобными для ревью.
Существующий ресурс Fallout FRM/FR0 FRM или FR0 Сохраняйте только когда проект может распространять источник и имеет маршрут визуальной регрессии.
Существующий ресурс Tactics ART/SPR FOFRM со ссылками на ART/SPR Параметры имени выбирают палитры, кадры, отражения, цвета и последовательности. SPR обычно следует оборачивать из-за границы штатного runtime.
Существующий ресурс Infinity Engine ZAR/TIL/MOS/BAM, обычно через FOFRM, если нужен выбор Считайте это путём импорта, а не рекомендуемым форматом для новой графики.

Встроенный image baker не поддерживает JPEG, BMP, GIF, DDS, WebP, AVIF и SVG. Перед запеканием преобразуйте такие входы в PNG или поддерживаемое подмножество TGA. Не делайте вывод о поддержке по renderer или сторонней библиотеке, которая случайно умеет читать формат: контракт определяют ImageBaker и выбранная runtime-реализация SpriteFactory.

Обзор конвейера

Обычный путь выглядит так:

  1. Resource pack публикуют исходные файлы через FileCollection.
  2. ImageBaker сканирует зарегистрированные расширения или получает один целевой путь.
  3. Выбранный loader возвращает FrameCollection с единым числом кадров/timing и одной последовательностью либо полным набором направлений.
  4. BakeCollection при необходимости строит и оценивает silhouette mesh для каждого уникального кадра, добавляет поля или обрезает RGBA-холст и определяет его логический корень.
  5. Он записывает частный контейнер RGBA/mesh-кадров по исходному пути либо под заданным loader NewName и поддерживает SpriteInfo/<PackName>.foinfo.
  6. В клиентском runtime SpriteManager выбирает factory по приведённому к нижнему регистру расширению, а DefaultSpriteFactory читает запечённые байты.
  7. Конкретные кадры попадают в запрошенный texture atlas; при необходимости metadata анимации/направлений становится SpriteSheet.

Исходные декодеры не исполняются в штатном клиенте. Файл Sprite.png в запечённом resource pack содержит контейнер спрайта Engine, а не исходные байты PNG. Сохранённое расширение является идентификатором диспетчеризации, а не обещанием формата запечённой нагрузки.

Грамматика FOFRM

FOFRM использует parser Engine ConfigFile. Корневой/default-раздел описывает однонаправленную последовательность. Направленные sheets используют разделы [dir_N] или [Dir_N].

Минимальное статическое изображение

count = 1
fps = 10
frm = Icon.png

Ненумерованный alias frm/Frm допускается только для ссылки с номером ноль. Нумерованные ключи яснее и масштабируются на анимации:

fps = 8
count = 3
frm_0 = Idle_00.png
frm_1 = Idle_01.png
frm_2 = Idle_02.png

Ссылки разрешаются относительно каталога .fofrm. Храните связанные исходные кадры рядом с descriptor или в стабильном относительном поддереве; не полагайтесь на текущий каталог машины разработчика.

Размещение последовательности и дельты кадров

Корень или каждое направление может задавать знаковые смещения размещения в одном из двух стилей имён:

offs_x = -24
offs_y = -63

или:

OffsetX = -24
OffsetY = -63

Если смещения направления пропущены, они наследуют значения, действовавшие при разборе предыдущего направления. Это бывает полезно для legacy-данных, но легко читается неверно. Production descriptor направлений должен явно записывать оба смещения в каждом разделе направления.

Дельты каждой ссылки используют next_x_N / next_y_N либо NextX_N / NextY_N:

next_x_0 = 2
next_y_0 = -1

Дельта descriptor прибавляется к собственным NextX и NextY каждого импортированного дочернего кадра. Это не обычные смещения размещения изображения. Многокадровые runtime sheets сохраняют их как смещение каждого кадра, используемое специализированным presentation-кодом. Подробная проекция ходьбы/ бега и граница authoritative movement описаны в Sprite Root Motion.

Наборы направлений

Направленный descriptor должен содержать либо одну последовательность, либо все направления от нуля до GameSettings::MAP_DIR_COUNT - 1:

fps = 10
count = 2

[dir_0]
offs_x = -24
offs_y = -63
frm_0 = Walk_NE_00.png
frm_1 = Walk_NE_01.png

[dir_1]
offs_x = -24
offs_y = -63
frm_0 = Walk_E_00.png
frm_1 = Walk_E_01.png

# Continue every configured map direction.

Каждое направление должно после flattening давать одинаковое итоговое число кадров. Неполный набор направлений, отсутствующий последующий раздел или иное число расплющенных кадров приводят к ошибке запекания.

Вложенные ссылки и flattening

Каждый frm_N может ссылаться на любое зарегистрированное расширение image loader и содержать параметры имени после $. Если дочерний ресурс имеет несколько кадров, FOFRM добавляет его последовательность Main в родителя. Он не компонует наборы направлений ребёнка и его OffsX/OffsY уровня последовательности. Вложенный дочерний кадр, уже являющийся shared record, отклоняется, потому что его индекс не перенумеровывается при flattening.

Различие между числом ссылок descriptor и расплющенным числом кадров существенно:

fps = 10
count = 2
frm_0 = FirstCycle.bam
frm_1 = SecondCycle.bam

FOFRM вычисляет полную длительность как 1000 * count / fps, а runtime делит эту длительность на расплющенное число кадров. Если каждая ссылка BAM добавляет несколько кадров, воспроизведение будет быстрее ожидаемого. Для предсказуемого темпа ссылайтесь на один статический кадр в каждом слоте descriptor либо рассчитывайте timing по фактическому итоговому числу и проверяйте его в видимом клиенте.

fps = 0 создаёт нулевое полное число ticks и намеренно отключает обычное воспроизведение. Для проигрываемого многокадрового sheet целочисленное AnimTicks / frame_count должно быть не меньше одной миллисекунды: runtime loop делит на это значение.

Устаревший ключ Effect

Parser принимает effect и Effect в FrameCollection::EffectName, но baker не сериализует и не применяет его, а штатный runtime не выбирает по нему shader. Считайте ключ игнорируемым compatibility-входом. Выбирайте эффекты проекта через принадлежащую renderer, prototype, GUI или script поверхность, описанную в Effect Format.

Устаревшие параметры имени файла

Параметры располагаются после $ и перед расширением. LoadAny удаляет их из физического пути поиска и передаёт выбранному loader.

ART

Actor$1THF5-7.art
  • 0-3 выбирают палитру; действует последний selector, а недоступная палитра заменяется нулевой.
  • T выводит alpha из максимального компонента RGB; индекс палитры ноль остаётся полностью прозрачным.
  • H и V отражают изображение по горизонтали и вертикали.
  • F5 выбирает кадр 5; F5-7 и F7-5 выбирают включительные возрастающий и убывающий диапазоны. Границы ограничиваются доступной таблицей кадров.
  • Регистр букв параметров не важен. Неизвестные символы игнорируются.

Флаг static в ART принудительно оставляет одну rotation. Вход с восемью rotation переназначается под текущую геометрию карты и становится полным набором направлений Engine.

SPR

Actor$[1,12,0,0][2,0,-8,4]Walk.spr

Parts: 0 — other, 1 — skin, 2 — hair, 3 — armor. Смещения RGB прибавляются и ограничиваются диапазоном 0..255. Значение part вне диапазона применяет RGB ко всем частям. Текст после последней скобки выбирает последовательность без учёта регистра; пустое имя выбирает первую.

SPR импортирует слоёные пиксели, удаляет недопустимые индексы кадров последовательности и создаёт shared records для повторяющихся индексов. Темп фиксирован на 10 fps. Штатная DefaultSpriteFactory не регистрирует .spr, хотя ImageBaker умеет его запекать. Обычно ссылайтесь на SPR из .fofrm, чтобы скомпонованный запечённый выход имел зарегистрированное расширение .fofrm. Прямые runtime-пути .spr требуют явной пользовательской sprite factory.

BAM

Spell$1.bam
Spell$1-3.bam

Первое целое выбирает cycle. Необязательное целое после - выбирает один кадр. Значения cycle или frame вне диапазона заменяются нулём. Без selector кадра импортируется весь cycle. Отрицательные значения выбранного кадра трактуются как форма всего cycle.

Подробности исходных форматов

PNG

PNG рекомендуется по умолчанию для авторских изображений проекта. Loader:

  • сокращает 16-битные каналы до 8 бит;
  • расширяет grayscale малой разрядности и palette pixels;
  • раскрывает прозрачность tRNS;
  • заполняет отсутствующий alpha значением 255;
  • создаёт один кадр RGBA8.

Повреждённый вход завершается через callback ошибок libpng. Явно определяйте в проекте политику color space и premultiplication: ImageBaker не предоставляет проектный workflow управления цветом.

TGA

Поддерживаемое production-подмножество намеренно узкое:

  • TrueColor type 2 (raw) или type 10 (RLE);
  • 24-битные BGR или 32-битные BGRA pixels;
  • без image ID;
  • origin снизу слева, поскольку реализация всегда переворачивает строки.

Indexed, grayscale и другая разрядность завершаются ошибкой. Loader не учитывает бит origin descriptor и не пропускает image ID, поэтому top-origin или ID-bearing файлы могут дать неверный либо отклонённый результат. Предпочитайте PNG, если существующий toolchain не имеет проверенного TGA preset.

Fallout FRM и FR0

FRM читает big-endian frame rate/count, смещения последовательности, дельты кадров и одну либо полную таблицу направлений. Файл .pal с тем же базовым именем переопределяет встроенную палитру Fallout. При стандартной палитре анимированные индексы палитры могут развернуть один источник в сгенерированный цветовой cycle; проверяйте итоговое число кадров и темп.

FR0 является точкой входа для разделённых соседних fr0, fr1 и последующих направлений. После начала нескольких направлений отсутствие следующего файла является ошибкой. Пути critter нормализуются в lowercase .fofrm, другие разделённые наборы — в .frm.

RIX, ZAR, TIL, MOS и BAM

  • RIX создаёт один непрозрачный кадр со встроенной палитрой.
  • ZAR создаёт один raw/RLE кадр с палитрой и alpha.
  • TIL импортирует вложенные кадры ZAR как последовательность 10 fps.
  • MOS/MOSC импортирует одно tiled-изображение, предварительно распаковывая MOSC; зелёный 0x00FF00 в палитре прозрачен.
  • BAM/BAMC импортирует выбранный cycle/frame, распаковывает BAMC, поддерживает RLE, выводит дельты кадров и считает синий 255 в палитре прозрачным.

Это compatibility-импортеры. Не выбирайте их для новой графики проекта, если PNG и FOFRM выражают тот же авторский замысел яснее.

Граница запечённого контейнера

Запечённый поток байтов — частное соглашение между ImageBaker и DefaultSpriteFactory, а не публичный формат сериализации проекта. Концептуально он содержит:

  1. SPRITE_RESOURCE_MAGIC (43) и SPRITE_RESOURCE_VERSION (2);
  2. little-endian uint16 числа кадров и полных animation ticks;
  3. uint8 числа направлений (1 либо GameSettings::MAP_DIR_COUNT);
  4. shared flag каждого кадра каждого направления;
  5. для конкретного кадра — знаковый int16 draw offset, uint16 обрезанных width/height, знаковые int16 NextX/NextY и ровно width * height * 4 байта RGBA;
  6. SpriteMeshKind; mesh record дополнительно хранит числа vertices/indices, логический размер источника и origin обрезки, локальные вершины фиксированной ширины и индексы треугольников;
  7. для shared frame — uint16 индекса более раннего кадра;
  8. завершающий SPRITE_RESOURCE_MAGIC.

Не разбирайте и не создавайте этот поток в скриптах проекта или внешних content tools. Передавайте поддерживаемые исходники закреплённому baker Engine. Изменение layout контейнера может координироваться внутри одной ревизии Engine без сохранения межревизионной совместимости байтов.

Runtime-загрузка, atlas и caches

SpriteManager приводит расширение пути к нижнему регистру и выбирает зарегистрированную SpriteFactory. Стандартная factory передаёт полный диапазон байтов в ReadSpriteResource, который проверяет magic, version, records, mesh geometry, footer и trailing data. Затем factory проверяет инвариант одного или полного набора направлений.

Ресурс с одним кадром и одним направлением становится AtlasSprite. Его OffsX/OffsY последовательности становится смещением размещения спрайта; сериализованные NextX/NextY читаются, но игнорируются. Многокадровый или направленный ресурс становится SpriteSheet. Каждое направление владеет параллельным sheet, а каждый concrete/shared кадр сохраняет отдельное смещение в _sprOffset.

SpriteSheet умеет выбирать направление, рандомизировать начальный кадр через Prewarm, задавать нормализованное время и проигрывать с loop или reverse. Play ничего не делает при одном кадре или нулевых полных ticks. Смена направления сама по себе не переписывает общие animation clock.

Конкретные RGBA-кадры размещаются в запрошенном AtlasType. Factory требует положительные размеры, загружает изображение, дублирует однопиксельные края для linear filtering и создаёт hit mask из alpha через Settings.SpriteHitValue. Тип атласа также входит в ключ cache копируемых спрайтов, поэтому один путь может иметь отдельные экземпляры interface/map/one-image.

При SpriteMesh.Enabled ImageBaker считает alpha > AlphaThreshold видимой маской, ищет кандидаты до MaxTriangles и через AreaSavingsWeight оценивает сэкономленную площадь исходного кадра относительно отправляемых треугольников. Каждый выбранный mesh должен покрывать все видимые pixel cells. Недопустимые или невыгодные кандидаты сохраняют обычный quad; пустая маска записывает явную пустую геометрию. Mesh frames могут использовать обрезанный или немного дополненный texture canvas, но их сериализованные offset, логический размер источника и source origin сохраняют размещение, scaling, интерполяцию map light и координаты hit-test.

Baker проверяет всю группу SpriteMesh.*, даже когда генерация mesh отключена. Поэтому standalone-конфиги проекта должны объявлять положительный MaxTriangles, AlphaThreshold в 0..254 и конечный неотрицательный AreaSavingsWeight; конфиг минимального проекта показывает явные стандартные значения для отключённого режима.

Обычные полноразмерные draw спрайта отправляют запечённый индексированный список треугольников. Region crops, tiled patterns, padded custom-effect/outline draws, fonts, blits, runtime model sprites и particles продолжают использовать прямоугольные пути. Точное поведение draw и dump атласа описано в Frontend и рендеринг, а политика кандидатов и поля baking report — в Baking Pipeline.

SpriteInfo/<PackName>.foinfo версии 1 — компактный индекс каждого pack с duration, directions, границами кадров, offsets и shared references. Общий EngineMetadata загружает его на server и client без декодирования RGBA payload. Появление или потеря этого агрегированного индекса требует полной перезапечки.

Copyable cache entries хранят prototype и возвращают MakeCopy(), чтобы вызывающие стороны не разделяли animation state. Отсутствующие файлы/расширения, неизвестные factories и ошибки загрузки отдельно мемоизируются по пути в _nonFoundSprites. CleanupSpriteCache() не очищает этот набор. После добавления ресурса, который запущенный клиент уже не смог загрузить, пересоздайте sprite manager или перезапустите клиент перед проверкой того же пути.

Практики авторинга

Для нового игрового контента:

  1. Предпочитайте PNG для пикселей и FOFRM для композиции анимаций/направлений.
  2. Используйте lowercase-расширения и стабильные пути с правильным регистром, хотя само расширение при диспетчеризации приводится к нижнему регистру.
  3. Храните одну семантическую анимацию в одном descriptor. Избегайте глубоко вложенных анимированных ссылок: count, flattening и timing сложнее ревьюить.
  4. Явно записывайте fps, count, каждый frm_N и оба offset. В направленном файле записывайте offsets в каждом направлении.
  5. Делайте все разделы направлений полными, а числа кадров — симметричными.
  6. Считайте NextX/NextY presentation-данными. Проверяйте root motion визуально и не используйте его как authoritative movement/collision state.
  7. Оборачивайте SPR-импорт в FOFRM, если проект намеренно не владеет custom factory.
  8. Преобразуйте неподдерживаемый или неоднозначный TGA export в PNG.
  9. Храните оригинальные legacy assets и custom palettes только когда лицензия разрешает распространение; документируйте provenance во встраиваемом проекте.
  10. В видимом клиенте проверяйте размеры, прозрачные поля, atlas filtering, click/hit masks, отображение направлений, темп и stop/start behavior.
  11. После изменения SpriteMesh.*, кода контейнера или mesh policy запускайте ForceBakeResources: timestamps источников не доказывают, что существующие выходы использовали те же settings. Перед приёмкой изменений triangle cost, padding, crop или fallback изучайте BakingReport.json и Game.DumpAtlases().

AI-автору следует предпочитать явные ключи FOFRM и ссылки на однокадровые PNG. Перед изменением существующей строки legacy-параметров изучите сгенерированный справочник options и focused native fixtures, а не угадывайте поведение по имени файла.

Диагностика

Симптом Вероятная граница Первые проверки
Targeted bake ничего не записывает и не сообщает ошибку Неподдерживаемая/отсутствующая цель или пропуск BakeChecker Проверьте расширение, source pack, регистр пути, timestamp/cache policy и видит ли файл полный scan.
Image file not found Относительная ссылка FOFRM Разрешите путь от каталога descriptor и удалите $... перед проверкой физического пути.
FOFRM file invalid data Отсутствующий slot, неравные направления, пустой child или неполный набор направлений Сравните count, каждый frm_N, все разделы направлений и итоговые числа дочерних кадров.
FOFRM file invalid data (shared index) Вложенный child уже использует shared records Выполните flattening из конкретных источников либо уберите слой вложенной анимации.
Прямой путь .spr сообщает неизвестное расширение Граница штатной runtime factory Загружайте запечённую .fofrm-обёртку либо регистрируйте принадлежащую проекту custom factory.
Спрайт остаётся отсутствующим после добавления файла Мемоизация _nonFoundSprites Перезапустите/пересоздайте sprite manager клиента и подтвердите наличие запечённого выхода.
Анимация не проигрывается Один кадр, нулевые ticks или недопустимый эффективный темп Проверьте итоговое число кадров и AnimTicks; вычислите целые ticks на кадр.
При смене направления неверное framing Унаследованные/пропущенные offsets или remap исходных направлений Сделайте offsets явными в каждом разделе и визуально проверьте все направления.
TGA перевёрнут или смещён Неподдерживаемые допущения origin/image ID Экспортируйте с поддерживаемым preset либо преобразуйте в PNG.
Alpha fringe или неверная hit area Source alpha, граница атласа или SpriteHitValue Проверьте raw alpha и итоговый atlas-backed sprite в нужном client profile.

Во время scan независимые ошибки изображений журналируются как Image baking error, а после завершения всей выбранной работы baker выбрасывает одно агрегированное исключение Errors during images baking. Исправьте каждый исходный файл: агрегированное число не является первопричиной.

Порядок проверки

Для изменений документации/модели:

python BuildTools\docs_image_format.py --write
python BuildTools\docs_image_format.py --check
python -m unittest BuildTools.tests.test_docs_image_format
python BuildTools\docs_contract_diff.py --help
python BuildTools\docs_validate.py

Для изменений importer, container, atlas или playback запустите focused native image-baker и texture-atlas coverage через настроенную цель RunUnitTests. Test_ImageBaker.cpp покрывает targeted/scan baking, поведение BakeChecker, PNG/TGA, каждый legacy importer, options, flattening/directions FOFRM, shared records, переименование выхода и повреждённые входы. Test_TextureAtlas.cpp покрывает split/search/free allocator.

Затем проверьте затронутый встраиваемый проект:

  1. Перегенерируйте image-format model/reference и просмотрите агрегированный diff контракта image-format.
  2. Перезапеките затронутые ресурсы закреплённой ревизией Engine проекта.
  3. Запустите узкие project resource/prototype checks, разрешающие эти пути.
  4. Запустите видимую клиентскую сцену, использующую каждое изменённое изображение, анимацию, направление, отражение, alpha edge, hit area и поддерживаемый renderer/profile.
  5. Для locomotion offsets также следуйте Sprite Root Motion и проверьте прямое движение, повороты, смену направления и переходы stop/start.

Native tests доказывают инварианты декодирования и контейнера. Они не доказывают framing графики проекта, приоритет resource pack, визуальный темп, filtering, clickability или воспринимаемое скольжение ног.

Checklist изменения

При изменении image surface:

  • обновите BuildTools/ImageFormatInterface.json и это руководство в одной правке Engine;
  • перегенерируйте Docs/generated/image-format.json и все generated image-страницы;
  • запустите BuildTools/tests/test_docs_image_format.py, полный documentation suite и docs_contract_diff.py относительно требуемой базы;
  • запустите focused native tests затронутой границы loader/runtime/atlas;
  • обновляйте Baking Pipeline, Client Runtime или Sprite Root Motion, только когда меняется принадлежащее им поведение;
  • обновляйте документацию встраиваемого проекта только для конкретных assets, policy, integration и изменений видимой проверки;
  • закрепляйте public examples на точной ревизии Engine и не обещайте поддержку формата или option, которую не исполняет их validation route.
Введите запрос.