Исследование от 29.09.2026. В raw/ и в проекте ничего не менялось. Все скрипты, данные и картинки лежат в
scratchpad/research/assets/ (далее assets/). Окружение: Mac arm64, 8 ядер, 8 ГБ ОЗУ. Отдельный venv
assets/.venv (uv, Python 3.12.3): texture2ddecoder 1.0.6, Pillow 12.3.0, numpy 2.5.3, pyoxipng 9.1.1,
pngquant 3.0.3 (пакет pngquant-cli), imageio-ffmpeg 0.6.0 (ffmpeg 7.1 с libopus, aac_at и libmp3lame) и brotli 1.2.0.
cwebp 1.6.0 взят из Homebrew, он уже был установлен. Глобально ничего не ставилось.
Обозначения: [проверено] — подтверждено командой, числом или картинкой; [предположение] — вывод без прямой проверки.
- Альфа — подтверждено. У всех 3 405 текстур с удвоенной высотой нижняя половина строго серая: R=G=B у 100% пикселей, максимальная разница каналов 0. Это маска, выровненная с цветом пиксель в пиксель. Альфа прямая, не premultiplied. У 703 текстур (все карты и фоны) маска целиком равна 255, то есть альфа им не нужна. 98 текстур без удвоения — непрозрачные изображения.
- Атласы — подтверждено с уточнениями. Поля такие:
x y w h— прямоугольник в цветной половине страницы,ox oy— смещение обрезанного кадра от левого верхнего угла исходного кадра (Y вниз),W H— исходный размер кадра. Сверх гипотезы найден 10-й необязательный флагr(1 340 кадров из 149 933). Такой кадр хранится повёрнутым на 90° по часовой, а парыw,hиox,oyу него записаны переставленными. - Объём браузерного комплекта: около 439 МБ при WebP q90 + WebP-иконках + Opus + сжатых данных. Сейчас оригинал занимает 559 МБ, PNG без оптимизации — 1,21 ГБ.
- На старте: примерно 17–21 МБ (плюс JS-код) и ещё около 4 МБ до первого боя.
- Хостинг. Рекомендую держать на GitHub Pages только код, а ассеты не публиковать, а собирать в браузере из XAPK, который игрок приносит сам (см. раздел 5). Если публиковать ассеты всё же решено, подходит Cloudflare Workers/Pages или R2. GitHub Releases не отдают CORS, а LFS не работает с Pages.
Цепочка (реализация — assets/pkm.py): заголовок CCZ! → zlib → PKM → ETC1 → RGB → разбиение на
цвет и альфу. Декодер ETC1 — texture2ddecoder.decode_etc1: он возвращает BGRA, альфа равна 255.
Заголовки всех 3 503 файлов [проверено] (assets/texture_headers.json):
- CCZ: сигнатура
CCZ!, тип сжатия 0 (zlib) и версия 2 у всех, резерв 0, длина совпадает с распакованной. - PKM:
PKM 10, формат 0 (ETC1 RGB) у всех. Размер данных у всех ровно(W/4)·(H/4)·8байт. - Высота: у 3 405 закодированная высота ровно вдвое больше исходной, при этом ширина совпадает, а исходная высота кратна 4, так что половины не делят блоки 4×4. У 95 высота не удвоена. Ещё у 3 иконок зданий 456×280 при исходных 455×279 — это просто дополнение до кратного 4.
- Все размеры — степени двойки, кроме этих 3 иконок и
maps/city/city_1(3392×2452). - Всего: 455 МБ CCZ, 2,28 ГБ распакованного ETC1, 4 563 Мпикс закодировано, из них 2 299 Мпикс видимых.
- Уникальных по содержимому 3 336. 167 файлов — побайтовые дубли (9,1 МБ CCZ). Например,
buildings/X/w_1.*совпадает сbuildings/X/w/w_1.*, а одинаковые тайлы встречаются в разных картах.
Скорость [проверено]: текстура 2048×2048 декодируется за 0,03 с. Проверка серости по всем 3 405 текстурам заняла 14 с в 6 процессах. Полный прогон (декод и 5 кодировок) — 648 с.
Образцы (assets/samples/): для каждого есть *_etc_full.png (сырой декод обеих половин),
*_rgba.png и *_preview.png (верх | низ | RGBA на шахматке).
| Образец | Текстура | Размер (закод.) |
|---|---|---|
| building_acid_barrel | buildings/acid_barrel_1_1/w_1 | 1024×512 |
| building_champion | buildings/champion_1/w/w_1 | 2048×2048 |
| unit_bomb_pig | units/bomb_pig/bomb_pig_1 | 1024×1024 |
| mob_bug | mobs/bug/bug_3 | 1024×2048 |
| citizen_colonist | citizens/colonist_female_1/..._1 | 512×512 |
| turret_gun | turrets/gun_1/gun_1_2 | 2048×2048 |
| effect_air_strike | effects/air_strike/air_strike_1 | 2048×1024 |
| bg_preloader | background/preloader_art/..._0_0 | 1024×2048 |
| map_mission_tile | maps/mission/ch_1_s_1_map_04_0_0 | 1024×2048 |
| ui_button_close, ui_city_scene | interface/1024x768/city/... | 256×512, 2048×4096 |
| без удвоения: noalpha_icon_base, _icon_attraction, _road, _reactive_gun_alpha, _map_star1_09 | icons/buildings, road_texture, effects, maps/mission | 256², 456×280, 256², 512², 2048² |
Подтверждена [проверено].
- Серость. У всех 3 405 текстур с удвоением нижняя половина строго серая: R=G=B у 100% пикселей,
максимальная разница каналов 0 (
assets/gray_check.py,gray_check.json). - Выравнивание. Для 8 образцов спрайтов и UI пересечение «альфа > 128» и «цвет не чёрный»
максимально при нулевом сдвиге среди сдвигов ±2 px (
samples/samples_report.json). Для эффекта air_strike эта метрика неинформативна из-за свечения, для карт и фонов — из-за альфы 255. - Визуально. В превью силуэты маски совпадают со спрайтами. Взрывы превращаются в мягкое свечение,
тени — в полупрозрачные. При увеличении ×4 на белом и пурпурном фоне нет тёмной или цветной каймы:
samples/unit_bomb_pig_edges_zoom.png,ui_city_scene_edges_zoom.png. - Альфа прямая, не premultiplied. У видимых пикселей часто max(RGB) > A+16: 73% у building_champion,
62% у air_strike. При premultiplied такое невозможно. Под
alpha=0цвет в основном чёрный: медиана 95% пикселей по 2 684 текстурам, 10-й перцентиль 70%. Следствие: при билинейной фильтрации без premultiply на краях возможна тёмная кайма, поэтому текстуры нужно премультиплицировать при загрузке (UNPACK_PREMULTIPLY_ALPHA_WEBGL; в PixiJS это, насколько мне известно, режим по умолчанию [предположение]). - У 703 текстур маска целиком равна 255: maps/mission (523), background/mission_prepare (102), maps/city (61), background/worldmap (9), preloader_art (4) и 4 иконки. Для браузера их стоит хранить как RGB — это главный выигрыш в lossy-WebP.
- Шум ETC1 в альфе. 0–7% пикселей имеют альфу 1–8 из 255 вдали от спрайтов (дальше 4 px от альфы > 32). Это хвосты теней и шум ETC1: он не виден, трогать его не нужно.
- 98 текстур без удвоения — непрозрачные изображения, альфа им не нужна:
- 92 иконки
icons/buildings: здание на фоне, 256² или 455×279; maps/mission/star_1_map_09(2048², мини-карта с чёрной виньеткой) иcity_map_final_fixed(4096²);maps/city/city_1(3392×2452) — то же изображение, что склейка 12 тайловcity_1_*: PSNR 39,6 дБ, средняя разница 0,43 уровня, то есть сжато отдельно. Одну копию можно не хранить;road_texture— тайл дороги 256²;effects/reactive_gun*/..._alpha— чёрный фон с яркими полосами. [Предположение] Они рисуются аддитивно, а_alphaв имени — это режим смешивания, а не маска.
- 92 иконки
Подтверждена с уточнениями [проверено] (скрипты assets/atlas*.py, картинки в assets/atlas_frames/).
Формат строки: /имя.png⇥x⇥y⇥w⇥h⇥ox⇥oy⇥W⇥H⇥[r], окончание строки CRLF. Первая строка — textures: X.png.
Всего 2 685 атласов (уникальных 2 630), 149 933 кадра, 129 694 уникальных прямоугольника: одинаковые кадры
упаковщик хранит один раз.
-
Обычный кадр.
x y w h— прямоугольник в цветной (верхней) половине страницы.ox oy— позиция обрезанного кадра в исходном кадре от левого верхнего угла, Y вниз.W H— исходный размер, он жеwidth/heightнаправления вconfig/visuals/*.xml(у bomb_pig 128×125). Условиеox+w ≤ W, oy+h ≤ Hвыполняется у всех кадров. -
Флаг
r(1 340 кадров, 0,9%). Кадр хранится повёрнутым на 90° по часовой, а поля заданы в повёрнутой системе. Реальный размер —h×w, реальное смещение —(oy, ox). Восстановление: повернуть область на 90° против часовой и поставить вx=oy, y=ox. Доказательства:- в этой интерпретации условие выполняется у всех 1 340 кадров, а «как есть» ломается у 559, «поменять только w и h» — у 351;
- при сравнении с соседними кадрами той же анимации вариант выигрывает в 1 538 из 1 576 случаев (97,6%), остальные — ничьи с разницей ≤ 0,02 или кадры взрывов;
- прямой пример:
research_center_3_e_45 (r)имеетw=180 h=146 ox=7 oy=54, а соседнийe_43—w=146 h=180 ox=54 oy=7.
Картинки:
rotated_frames_check.png,cut_rotated_research_center_e_45.png. -
Начало отсчёта — левый верхний угол. По анимациям из
visualsсоседние кадры ближе друг к другу при отсчёте сверху в 55 визуалах из 57 (1 ничья, 1 противоположный с 7 парами кадров). Независимое подтверждение даёт якорь:(-offsetX, -offsetY)направления и полигонhitAreaотносительно него точно накрывают спрайт. Дляacid_barrel_1_1hitArea занимает x 0…60, y −38…20, а спрайт — x +4…+53, y −36…+18 (cut_building_acid_barrel_w_0.png). У юнита якорь попадает под ноги (cut_unit_bomb_pig_e_27.png). -
Обрезка плотная. Зазор между краем кадра и bbox альфы > 0: 0 px у 48–65% сторон, 1 px у 22–39%, 2 px и больше примерно у 10% (выборка 250 атласов). [Предположение] Зазор в 1 px — это ETC1, обнуливший слабую альфу.
-
Упаковка без отступов. У 95% обрезанных кадров соседний прямоугольник вплотную (0 px). В UI-атласах кадры не обрезаны и стоят с шагом 2 px (
cut_ui_button_cross.png). При масштабировании с билинейной фильтрацией возможно просачивание соседних кадров. В оригинале было то же самое; если появятся швы, при сборке можно переупаковать с extrude 1–2 px. -
Аномалии, которые должен учесть загрузчик:
effects/tracers/tracers.atlasссылается на обычныйtracers.png, а не на pkm;maps/city/cells.atlasв заголовке называетatlas.png, хотя текстура —cells.pkm.ccz, и имена кадров идут без/. Текстуру надо искать по имени файла атласа, а не по заголовку;- 4 кадра
interface/*/city/tutorial_menu.atlasвыходят за страницу на 2 px (x=2, w=2048) — обрезать по краю; - 73 кадра пустые (w или h равно 0), 957 кадров не обрезаны;
- имена кадров не уникальны между HD- и SD-интерфейсом, поэтому пространство имён нужно вести по атласу.
-
Текстуры без атласа (820): maps/mission (535), background/mission_prepare (102), icons/buildings (96), maps/city (71), background/worldmap (9) и другие. Большие картинки нарезаны на тайлы и описаны XML
back_atlas(<frame img x y width height>), остальные используются целиком.
Перевод в JSON-формат TexturePacker (для PixiJS или Phaser) [предположение — проверить в выбранном
движке на research_center_3_e_45, эталон есть]:
- обычный кадр:
frame {x,y,w,h},spriteSourceSize {x:ox, y:oy, w, h},sourceSize {W,H},trimmed = (w,h) ≠ (W,H); - кадр с
r:rotated: true,frame {x, y, w:h, h:w},spriteSourceSize {x:oy, y:ox, w:h, h:w}; pivot = (-offsetX/W, -offsetY/H)из направления вvisuals.
Методика: assets/convert_all.py декодирует каждую уникальную текстуру и кодирует её пятью способами.
Файлы не сохраняются — пишутся только размеры в sizes_textures.jsonl. Маски, равные 255 везде,
кодируются как RGB. Кодировки:
- PNG через Pillow, zlib 6;
- oxipng
-o2; - pngquant
--quality=65-90 --speed 3; если качество недостижимо, берётся oxipng — так было у 58 из 3 336; cwebp -lossless;cwebp -q 90(альфа остаётся lossless).
Полная выдача — assets/summary.txt.
Текстуры браузерного комплекта [проверено]: 3 242 уникальных файла. Исключены 93 текстуры SD-интерфейса
interface/1024x768_sd и дубль city_1. 166 дублей по путям уже не учитываются (в WebP это 8,3 МБ);
в клиенте их можно раздавать через манифест.
| Категория | Файлов | Мпикс | CCZ (оригинал) | PNG | PNG+oxipng | PNG8 pngquant | WebP LL | WebP q90 |
|---|---|---|---|---|---|---|---|---|
| buildings | 1 757 | 543 | 124,4 | 319,5 | 268,1 | 94,5 | 154,0 | 118,4 |
| maps/mission | 525 | 573 | 84,5 | 199,6 | 155,1 | 118,0 | 116,3 | 49,0 |
| units | 208 | 241 | 79,3 | 207,7 | 168,3 | 84,0 | 115,3 | 83,8 |
| mobs | 192 | 217 | 76,9 | 198,5 | 161,6 | 81,3 | 111,1 | 80,5 |
| interface HD | 92 | 313 | 17,2 | 36,1 | 31,0 | 16,8 | 20,7 | 13,3 |
| background | 115 | 121 | 17,1 | 33,3 | 26,6 | 22,7 | 20,1 | 7,2 |
| effects | 136 | 39 | 12,4 | 28,8 | 24,1 | 7,7 | 14,4 | 10,7 |
| citizens | 44 | 17 | 7,8 | 20,9 | 16,9 | 10,0 | 11,5 | 8,8 |
| maps/city | 64 | 66 | 9,1 | 19,1 | 15,3 | 11,4 | 11,8 | 4,5 |
| turrets | 12 | 21 | 6,6 | 19,1 | 15,7 | 7,3 | 9,3 | 7,9 |
| icons (ETC) + road | 97 | 6 | 2,3 | 5,5 | 4,4 | 3,6 | 3,3 | 1,6 |
| Итого, МБ | 3 242 | 2 155 | 437,7 | 1 088,1 | 886,9 | 457,1 | 588,0 | 385,8 |
Если для каждого файла выбирать меньшее из WebP q90 и PNG8, выйдет 333,6 МБ. SD-интерфейс занял бы ещё 5,0 МБ в WebP q90 (81 Мпикс).
Качество lossy [проверено] на 6 образцах, эталон — lossless-декод, который сам уже прошёл через ETC1. PSNR premultiplied RGB:
| Образец | WebP q90 | pngquant |
|---|---|---|
| здание | 44,8 дБ | 41,2 дБ |
| юнит | 35,4 дБ | 35,5 дБ |
| эффект | 49,8 дБ | 43,5 дБ |
| UI | 41,7 дБ | качество недостижимо |
На непрозрачных картах WebP q90 даёт 37,8–40,2 дБ при 189–205 КБ, а PNG8 — 34–39 дБ при 447–520 КБ.
При увеличении ×3 WebP q90 визуально неотличим от исходника, а у pngquant в мягких ореолах (взрывы,
брызги) заметен зернистый дизеринг: assets/quality/*_compare.png. Поэтому рекомендую WebP q90.
Прочее [проверено]:
- Готовые PNG (1 952 файла, 98,6% уже индексированные PNG8): как есть 68,6 МБ, oxipng 63,4, WebP lossless 57,1, WebP q90 32,6.
- Звук (178 файлов, 32,6 мин; MP3 128 кбит/с, WAV моно 22 кГц и стерео 44,1 кГц): как есть 47,6 МБ.
- Opus (музыка 64k, моно SFX 32k, стерео SFX 48k) — 14,8 МБ.
- AAC (96k, 48k, 64k) — 22,3 МБ.
- MP3 96k/64k — 22,1 МБ.
- Главная экономия — на
actions/*.wav: 14 МБ → около 1 МБ.
- Данные — всего около 5,4 МБ в gzip:
- атласы 9,1 МБ → 1,4 МБ gzip;
config.json14,1 МБ → 0,36 gzip, 0,20 brotli;config/maps12,9 → 1,18;- миссии 3,9 → 0,40;
- локали 1,6 → 0,30;
- visuals 4,1 → 0,3–0,8;
- ccbi 0,6 → 0,24;
- TTF 0,6 → 0,22.
Полный комплект, МБ [проверено; состав — моё решение]:
| Вариант | Текстуры | PNG-иконки | Звук | Данные | Итого |
|---|---|---|---|---|---|
| Как есть (CCZ + PNG + WAV/MP3) | 437,7 | 68,6 | 47,6 | 5,4 | 559,4 |
| PNG без оптимизации | 1 088,1 | 68,6 | 47,6 | 5,4 | 1 209,8 |
| PNG + oxipng, AAC | 886,9 | 63,4 | 22,3 | 5,4 | 978,0 |
| PNG8 pngquant, AAC | 457,1 | 63,4 | 22,3 | 5,4 | 548,2 |
| WebP lossless, Opus | 588,0 | 57,1 | 14,8 | 5,4 | 665,3 |
| WebP q90, Opus (рекомендую) | 385,8 | 32,6 | 14,8 | 5,4 | 438,6 |
| WebP q90, AAC | 385,8 | 32,6 | 22,3 | 5,4 | 446,1 |
| min(WebP q90, PNG8), Opus | 333,6 | 32,6 | 14,8 | 5,4 | 386,4 |
Если держать на сервере и Opus, и AAC, выйдет около 461 МБ, но каждый клиент качает только один формат.
Файлов около 8–10 тысяч: 3 242 текстуры, до 2 630 JSON-атласов, 1 952 иконки, 178 звуков и данные.
Самый большой файл в WebP — 1,02 МБ (city_map_final_fixed).
Старт [объёмы проверены; состав набора — предположение]. Набор собран по
initial_users/default:
- постройки base →
base_1, trade_area →wait_1, citizen_house →colony_house_1, invasion_house →barriers_2_1; - фон загрузки
preloader_art, карта колонии (12 тайлов, substrate, cells, blue_circle), колонисты; - иконки ресурсов и аватаров, 91 UI-звук, городская тема 01 и
ambient_city.
Каким будет реальный набор, выяснится, когда появится загрузчик.
| Набор | Текстуры WebP q90 (CCZ) | PNG | Звук Opus (AAC) | Данные | Итого |
|---|---|---|---|---|---|
| Минимальный старт (UI: splashscreen, mainmenu, city) | 12,3 (16,5) | 1,0 | 1,8 (2,9) | ~2 | ~17 МБ |
| Старт со всем HD-интерфейсом (+battle, worldmap) | 16,4 (21,6) | 1,0 | 1,8 (2,9) | ~2 | ~21 МБ |
Дозагрузка к первой миссии (tutorial_01_map_01, туториальные юниты и мобы, авиаудар, все actions, боевая тема) |
2,5 | — | 1,9 | — | ~4,4 МБ |
К этому добавляется JS-код движка. Сеть не узкое место, узкое место — видеопамять. Стартовые текстуры в RGBA8 занимают 1,05–1,36 ГБ (263–339 Мпикс), почти всё приходится на HD-интерфейс: 92 текстуры, 313 Мпикс, в основном 2048² с пустыми областями. Весь комплект занял бы 8,6 ГБ. В оригинале ETC1 занимал 0,5 байта на пиксель на каждую половину. Что с этим делать:
- грузить и выгружать UI по экранам;
- на мобильных использовать SD-интерфейс (81 Мпикс);
- позже перевести тяжёлые категории на KTX2/Basis — это не измерялось.
Лимиты проверены 29.09.2026 по официальной документации.
| Площадка | Лимиты | Источник |
|---|---|---|
| GitHub, репозиторий | Предупреждение на файлах > 50 МиБ, блок > 100 МиБ, загрузка через браузер ≤ 25 МиБ. Репозиторий «в идеале < 1 GB», «настоятельно < 5 GB». Размер на диске 10 GB, push ≤ 2 GB, в каталоге ≤ 3 000 записей. | about-large-files, repository-limits |
| Git LFS | Free и Pro: 10 GiB хранения и 10 GiB трафика в месяц; Team и Enterprise: 250/250. Файл ≤ 2 GB (Free/Pro), 4 GB (Team), 5 GB (Enterprise). LFS нельзя использовать для сайтов GitHub Pages. Цены сверх квоты на странице документации не указаны. В сниппете поиска фигурируют $0,07 за ГиБ·мес хранения и $0,0875 за ГиБ трафика — не подтверждено. На github.com/pricing — «$5 за 50 GB». | LFS billing, about LFS |
| GitHub Releases | Файл < 2 GiB, до 1 000 ассетов в релизе, общий размер и трафик не ограничены. Проверено curl: ассет редиректит на release-assets.githubusercontent.com и отдаётся без Access-Control-Allow-Origin, поэтому fetch() из браузерной игры с другого домена не сработает. |
about-releases |
| GitHub Pages | Сайт ≤ 1 GB, исходный репозиторий рекомендуется ≤ 1 GB, трафик — мягкий лимит 100 GB в месяц, 10 сборок в час, деплой ≤ 10 мин. Не для коммерческих сервисов. Проверено: access-control-allow-origin: *, cache-control: max-age=600. |
pages-limits |
| Cloudflare Pages | 20 000 файлов на сайт (Free), 100 000 на платных тарифах; файл ≤ 25 МиБ; 500 сборок в месяц (Free). | pages limits |
| Cloudflare Workers Static Assets | «Requests to static assets are free and unlimited». 20 000 файлов на версию (Free), 100 000 (Paid); файл ≤ 25 МиБ. | billing-and-limitations, workers limits |
| Cloudflare R2 | Free: 10 GB·мес, 1 млн операций класса A и 10 млн класса B в месяц, исходящий трафик бесплатный. Сверх квоты: $0,015 за GB·мес, $4,50 за млн A, $0,36 за млн B. Объект ≤ 5 GiB. Адрес r2.dev ограничен по частоте и предназначен только для разработки; для продакшена нужен свой домен, и только с ним работает кэш. |
r2 pricing, public buckets, r2 limits |
Что это значит для рекомендуемого комплекта (438,6 МБ, около 10 тысяч файлов, самый большой 1 МБ):
- GitHub Pages технически вмещает комплект. Но ассеты пришлось бы коммитить в git: история раздувается, и после пары обновлений репозиторий перевалит за 1 GB. 100 GB трафика в месяц — это около 230 полных скачиваний.
- Releases не подходят для прямой загрузки из браузера: нет CORS.
- LFS не отдаётся через Pages, а 10 GiB трафика — это около 20 клонов.
- Cloudflare Workers/Pages вмещают комплект с запасом (лимит — 20 тысяч файлов по 25 МиБ), статика
бесплатна и не ограничена, а заливка через
wranglerидёт прямо из локальной сборки, минуя git. R2 — альтернатива для крупных редко меняющихся данных: 10 GB бесплатно, но нужны свой домен и CORS.
Рекомендация. Главное ограничение не техническое. В README проекта сказано, что графика, звук и данные игры принадлежат правообладателям и в репозиторий не входят. Любая публичная раздача сконвертированных ассетов — это их распространение (на GitHub, кроме того, есть риск DMCA для всего репозитория).
- Основной путь — «свой XAPK». На GitHub Pages лежит только клиент.
- Игрок выбирает свой XAPK или OBB.
main.13(480 МБ) иpatch.59(154 МБ) — обычные ZIP с Deflate и Stored [провереноunzip -v]. - Клиент читает нужные записи через
File.sliceпо центральному каталогу ZIP. CCZ распаковывается черезDecompressionStream('deflate'), это zlib-обёртка. ETC1 декодируется в Web Worker (JS или WASM), маска берётся из нижней половины. Всё кэшируется в OPFS или IndexedDB. - Декод дешёвый: 0,03 с на 2048² в нативном коде. [Предположение] В WASM в пределах ×2–3.
- Экономия на хостинге — 100%, и это согласуется с политикой README.
- Игрок выбирает свой XAPK или OBB.
- Если публиковать ассеты всё же решено (с разрешения или для закрытого теста):
- хостинг — Cloudflare Workers Static Assets или Pages через
wrangler deployиз локальной сборки, либо R2 со своим доменом; - формат — WebP q90 (RGB для карт и фонов, RGBA для спрайтов), атласы в JSON, звук Opus с запасным AAC (в Safari на macOS поддержка Opus частичная, caniuse), данные в JSON с brotli или gzip от CDN;
- раскладка — сохранить дерево каталогов (лимит 3 000 записей в каталоге GitHub), имена с хешем
содержимого и манифест (дедупликация 166 путей),
Cache-Control: immutable, Service Worker.
- хостинг — Cloudflare Workers Static Assets или Pages через
- Загрузка в клиент в любом варианте:
- пакетами по сценам: старт около 17–21 МБ, миссия несколько МБ, постройки по требованию;
- premultiply при загрузке текстур;
- выгрузка текстур при уходе со сцены;
- SD-интерфейс для мобильных;
- при швах на кадрах — переупаковка атласов с extrude.
- Код:
assets/pkm.py— чтение CCZ и PKM, ETC1, альфа;decode_samples.py,gray_check.py— шаги 1–2;atlas.py— разбор и проверка всех атласов;atlas_check.py,atlas_seq_test.py,atlas_rot_test.py— смещение и поворот;atlas_cut_examples.py— вырезка кадров;convert_all.py,icons_sizes.py,audio_sizes.py,summarize.py— объёмы.
- Данные:
texture_headers.json,texture_hash_groups.json,sizes_textures.jsonl,sizes_png.json,sizes_audio.json,sizes_text.json,gray_check.json,summary.txt,browser_set.json. - Картинки:
assets/samples/(16 образцов, превью и увеличения краёв),assets/atlas_frames/(вырезки кадров и проверка поворота),assets/quality/(сравнение WebP и pngquant).