Создание сборки своей игры на базе CRYENGINE 5.3. Часть вторая. Запаковка ресурсов.

06. декабря 2017 CRYENGINE 5 0

Официальная документация по теме:

http://docs.cryengine.com/display/CEPROG/Compiling+Assets+for+Multiple+Platforms

В первой части сказа о создании сборки своей игры речь шла о подготовке к запаковке ресурсов игры, а в этой непосредственно осуществим её.

Запаковка ресурсов в основном оптимизирует занимаемое игрой дисковое пространство, а так же позволяет при желании зашифровать файлы игры. Не смотря на некоторую трудоёмкость процесса, рекомендую запаковывать ресурсы игры, чтобы было красиво и занимало меньше места у игроков. В CRYENGINE для этого используются zip-архивы с несильным сжатием и расширением pak, условимся называть их pak-архивами. Несильное сжатие предпочтительно для pak-архивов, так как это позитивно влияет на скорость запуска игры в виду экономии времени на распаковку движком.

Упакованный GameSDK от Crytek по умолчанию выглядел так:

 Примечание: В данном случае .cryasset.pak и их содержимое нужны для редактора и в сборке игры их быть не должно.

 

Разделение на паки применяется для снижения нагрузки на систему при распаковке-чтении движком и для оптимизации перекачки при шифровании pak-архивов. Можно не заморачиваться и упаковать почти все ресурсы в один pak-архив, но с очень важным условием — размер pak-архива не должен превышать 2 Гб, иначе движок не сможет с ним работать.

У нас получится примерно так:

Или вот так, когда по ленивому всё в один pak, но при условии, что он будет менее 2 Гб:


Способы запаковки.

Их три:

  1. Запаковка специальным скриптом (RC Jobs) rcjob.xml для rc.exe (компилятор ресурсов движка);

  2. Вывести используемый игрой контент с помощью Save Level Resources, а потом долго и упорно запаковывать вручную, используя свободный архиватор 7-zip.

  3. Комбинированный. Совместить способ запаковки с помощь скрипта rcjob.xml для rc.exe с ручной запаковкой с помощью Save Level Resources. Для этого достаточно будет сохранить используемые ресурсы с применением Save Level Resources, почистить от лишнего и докинуть недостающее, а потом применить rcjob.xml. Метод подойдёт для захламлённого проекта.

 

1. Запаковка с использованием скрипта (RC Jobs) для rc.exe.

Примечание: Это оптимальный метод для проекта, в котором используются только необходимые ресурсы из GameSDK или не используются вовсе. Иными словами, проект не содержит в себе разнообразный мусор, надёрганный и напиханный в процессе работы.

Официальная документация: http://docs.cryengine.com/display/CEPROG/Compiling+Assets+for+Multiple+Platforms

Основная суть этого метода заключается в автоматизации процесса копирования и запаковки ресурсов, что позволит после настройки скрипта запаковывать ресурсы игры в пару кликов. В данном случае запакуем содержимое каталога assets.

На первый взгляд будет казаться чем-то жутким и сложным, но на деле всё довольно просто и понятно.

Для осуществления задуманного нам потребуется:

  • bat-файл, который будет запускать rc.exe с командами из скрипта rcjob.xml, осуществляя сборку под указанную платформу и с указанным числом потоков процессора.

  • rcjob.xmlэто набор команд для rc.exe, перечисленный в текстовом файле, по копированию и запаковке указанных типов ресурсов. То есть сначала все необходимые файлы будут скопированы в промежуточный каталог, а затем будут упакованы в pak-архивы. Так больше контроля и 100% защиты от сбоев с повреждением файлов. Для заполнения файла используется синтаксис xml.

  • Если используются ресурсы из GameSDK, то все необходимые для игры (в данном примере в каталоге assets) должны быть извлечены из pak-архивов, кроме содержимого level.pak для уровней и pak-архивов локализации.

 

Создание bat-файла.

Это обычный текстовый файл с расширением .bat. Создаём его в любом удобном месте, лучше рядом со сборкой, чтобы не потерять. Название любое, написанное на латинице и без спецсимволов. Пример:

D:\MyBestGameBuild\PC.bat

Где D:\MyBestGameBuild это каталог для формирования сборки игры. Располагаем его ближе к корню, чтобы не столкнуться с ограничением Windows на количество символом в пути. Название любое на латинице, без пробелов и спецсимволов.

Содержимое записывается в одну строку:

"D:\Programs\CRYENGINE Launcher\Crytek\CRYENGINE_5.3\Tools\rc\rc.exe" /job="D:\MyBestGameBuild\rcjob_PC.xml" /p=pc /threads=4 > pclog.log

Описание содержимого:

«D:\Programs\CRYENGINE Launcher\Crytek\CRYENGINE_5.3\Tools\rc\rc.exe» — указываем путь до rc.exe, который поставляется вместе с движком. Кавычки нужны для корректного чтения интерпретатором путей с пробелом.

/job=»D:\MyBestGameBuild\rcjob_PC.xml» — указываем путь до xml-файла с командами, который имеет название rcjob_PC.xml. Само название файла не принципиально лишь бы латиницей, без спецсимволов и пробелов. О его содержимом упоминается дальше по статье.

/p=pc — указываем целевую платформу. В данном случае подразумевается Windows.

/threads=4 — количество потоков процессора, которые будет использовать rc.exe. Чтобы не было тормозов, имеет смысл указать число физических ядер процессора, а не все имеющиеся потоки.

> pclog.log — вывести список операций в процессе работы rc.exe в текстовый файл. Очень полезно при диагностировании проблем с выполнением команд.

Примечание: Перед и после знака > нужен пробел.

Создание rcjob_PC.xml.

Это текстовый файл, содержимое которого описано с применением xml-синтаксиса. Создаём его в любом удобном месте, лучше рядом с PC.bat. Название любое, написанное на латинице и без спецсимволов. Пример:

D:\MyBestGameBuild\rcjob_PC.xml

Для редактирования рекомендую использовать Notepad++ или любой другой текстовый редактор с указанием номеров строк и подсветкой синтаксиса. Можно обойтись стандартным блокнотом, но будет существенно менее удобно.

Расписывать по особенностям xml-синтаксиса в данной статье не буду, оставляю это для самостоятельного ознакомления, но уточню, что быть программистом не обязательно, там всё довольно просто.

Содержимое.

Примечание: Ниже приведённый код является примером, для личного использования необходимо указать актуальные для конкретной ситуации данные.

Примечание: Код может «скакать» по строкам при отображении на странице блога, но на деле строки строго определены. Рекомендую использовать Notepad++ или его аналоги для корректного просмотра кода.

<RCJobs>
<DefaultProperties src="D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject" game="assets" trg="D:\MyBestGameBuild\${p}\temp" pak_root="D:\MyBestGameBuild\${p}\MyBestGame" />

<Properties src_game="${src}\${game}" trg_game="${trg}\${game}" pak_game="${pak_root}\${game}" />

<CopyJob>
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cgf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cga" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.mtl" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.dds" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.1" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.2" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.3" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.4" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.5" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.6" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.7" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.8" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.9" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.1a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.2a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.3a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.4a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.5a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.6a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.7a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.8a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.9a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.adb" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.animevents" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.anm" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.bnk" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.bspace" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.caf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cax" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cdf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.chr" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.chrparams" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cfg" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.comb" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.dba" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.ent" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.lua" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.skin" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.swf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.thread_config" copyonly="1" />
    <Job sourceroot="${src_game}\languages" targetroot="${pak_game}\languages" input="*.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_01" targetroot="${pak_game}\Levels\MyBestLevel_01" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_02" targetroot="${pak_game}\Levels\MyBestLevel_02" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_03" targetroot="${pak_game}\Levels\MyBestLevel_02" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_01" targetroot="${pak_game}\Levels\MyBestLevel_01" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_02" targetroot="${pak_game}\Levels\MyBestLevel_02" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_03" targetroot="${pak_game}\Levels\MyBestLevel_03" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.xml" copyonly="1" />
</CopyJob>

<PakJob>
    <Job sourceroot="${trg_game}" input="Animations\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Audio\*.*" zip="${pak_game}\MyBestGame_Audio.pak" />
    <Job sourceroot="${trg_game}" input="config\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="difficulty\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="entities\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="libs\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="materials\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="prefabs\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="_newpreview_.mtl" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="autoexec.cfg" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="entities.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="hud_settings.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Savegames.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Objects\*.*" zip="${pak_game}\MyBestGame_Objects.pak" />
    <Job sourceroot="${trg_game}" input="Scripts\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Textures\*.*" zip="${pak_game}\MyBestGame_Textures.pak" />
</PakJob>

<Run Job="CopyJob"/>
<Run Job="PakJob"/>

</RCJobs>

 

Описание содержимого.

Примечание: В данном примере рассматриваются параметры с названиями из официальной документации, но можно задавать свои названия для параметров.

Примечание: Для удобства ориентирования в коде рекомендую скопировать его в Notepad++ или любой другой аналог, где будет включено отображение номеров строк.

Строка 2:

<DefaultProperties src=»D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject» game=»assets» trg=»D:\MyBestGameBuild\${p}\temp» pak_root=»D:\MyBestGameBuild\${p}\MyBestGame» /> — определяем значения для параметров scr, game, trg и pak_root.

src — это путь до рабочего проекта, с которым работали в редакторе, откуда будут браться ресурсы для копирования и запаковки.

game это название каталога, где находятся ресурсы игры. По умолчанию это каталог assets.

trg — это путь до промежуточного каталога, в который будут скопированы ресурсы для упаковки в pak-архивы.

pak_root — это путь для сохранения готовых pak-архивов. В примере пути можно заметить ${p} — это имя для каталога по названию платформы, которую указали в PC.bat. В данном случае название каталога будет pc. В итоге путь будет такой: D:\MyBestGameBuild\pc\MyBestGame. В данном случае MyBestGame это название каталога по имени игры. Можно обойтись без использования ${p} и написать прямо — pc.

Строка 4:

<Properties src_game=»${src}\${game}» trg_game=»${trg}\${game}» pak_game=»${pak_root}\${game}» /> — определяем значения для дополнительных параметров.

src_game=»${src}\${game}» — путь до рабочего проекта с переходом в каталог, который мы задали для параметра game во второй строке. В данном примере это каталог assets. Пример: D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject\assets

trg_game=»${trg}\${game}» — путь до промежуточного каталога с переходом в каталог, который мы задали для параметра game во второй строке. В данном примере будет создан каталог assets, так как он не был создан вручную. Пример: D:\MyBestGameBuild\pc\temp\assets

pak_game=»${pak_root}\${game}» — это путь для сохранения готовых pak-архивов с переходом в каталог assets, в который будут непосредственно сохранены pak-архивы. Пример: D:\MyBestGameBuild\pc\MyBestGame\assets

 

Описание содержимого элемента <CopyJob>…</CopyJob>.

CopyJob — это описание задачи копирования для rc.exe, где указывается источник ресурсов, из которого нужно скопировать обозначенный тип файлов в целевой каталог. rc.exe осуществляет рекурсивный поиск по всем каталогам источника и копирует все найденные файлы указанного типа. При этом копирование производится не просто всех файлов в кучу, а с созданием иерархии каталогов, в которых они были в каталогах источника.

Основной задачей по этому элементу будет описать все типы файлов, откуда их копировать и куда.

Строка 7:

<Job sourceroot=»${src_game}» targetroot=»${trg_game}» input=»*.cgf» copyonly=»1″ />

sourceroot=»${src_game}» — источник файлов для копирования. В данном примере это каталог assets в директории рабочего проекта. Пример: D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject\assets

targetroot=»${trg_game}» — каталог, в который будет осуществлено копирование ресурсов из источника. Пример: D:\MyBestGameBuild\pc\temp\assets

input=»*.cgf» — указываем тип файлов, которые будут найдены в источнике и скопированы. Как раз * указывает rc.exe, то нужно найти все файлы с расширением cgf. В данном случае это файлы статичной геометрии.

copyonly=»1″ — подтверждаем копирование.

В том же духе описывается копирование для прочих типов файлов:

Стоит учесть, что копирование по типу файлов (*.расширение_файла) копирует все файлы указанного типа, которые будут найдены в каталоге-источнике. Если такое не нужно, то можно указать явно из каких каталогов копировать указанный тип файлов.

Примеры:

  1. В каталоге-источнике могут быть сторонние pak-архивы и xml-файлы, которые нам не нужны в сборке. К примеру, компоненты уровня geometry.pak, terraintexture.pak, TimeOfDay.xml, Objectives.xml и прочие, которые используются в редакторе и не нужны в сборке, поэтому укажем явно какие pak-архивы и xml-файлы нужно скопировать из каталогов уровней.

  2. Тоже самое применимо и для файлов анимации .caf (и для всех остальных):

     

 

Описание содержимого элемента <PakJob>…</PakJob>.

Если указывать запаковку разных каталогов в один и тот же pak-архив, то они будут в него добавлены.

При желании упаковать всё в один pak-архив нужно учитывать, что предельный размер для упакованного pak-архива равен 2 Гб, при превышении этого лимита rc.exe не сможет его запаковать корректно.

Чтобы rc.exe не пытался упаковывать pakархивы уровней и локализации, придётся явно указать какие каталоги упаковывать. Так же явное указание каталогов для запаковки нужно для создания дополнительных pak-архивов, чтобы не превысить лимит в 2 Гб на pak.

sourceroot=»${trg_game}» указываем, что источником является каталог, в который в элементе <CopyJob>…</CopyJob> производилось копирование содержимого каталогов рабочего проекта. В примере каталог копирования это D:\MyBestGameBuild\pc\temp, а рабочий проект D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject.

input это каталог с файлами, который был скопирован из рабочего проекта. В примере мы явно указываем какой каталог в какой pak запаковывать.

zip указываем, что нужно запаковать и сохранить запакованный pak-архив по указанному пути с указанным именем. В примере это D:\MyBestGameBuild\pc\MyBestGame\assets.

Исходя из условий примера, будут созданы следующие pak-архивы и каталоги:

  • Каталог languages, в который были скопированы english_xml.pak и russian_xml.pak.

  • Каталог Levels, в который были скопированы каталоги уровней и их содержимое в виде filelist.xml и level.pak.

  • Pak-архив MyBestGame.pak, в который вошли Animations, config, difficulty, entities, libs, materials, prefabs, _newpreview_.mtl, autoexec.cfg, entities.xml, hud_settings.xml, Savegames.xml, Scripts.

  • Pak-архив MyBestGame_Audio.pak, в котором находится каталог Audio.

  • Pak-архив MyBestGame_Objects.pak, в котором находится каталог Objects.

  • Pak-архив MyBestGame_Textures.pak, в котором находится каталог Textures

 

Команды запуска выполнения действий rc.exe.

 
CopyJob — выполнить копирование, описанное в элементе <CopyJob>…</CopyJob>.

PakJob — выполнить запаковку скопированных ресурсов, запаковать их в pak-архивы и сохранить по указанному пути. Это было описано в элементе <PakJob>…</PakJob>.

Код rcjob_PC.xml целиком, финальный вариант.

<RCJobs>
<DefaultProperties src="D:\Programs\CRYENGINE Launcher\Crytek\MyBestGameProject" game="assets" trg="D:\MyBestGameBuild\${p}\temp" pak_root="D:\MyBestGameBuild\${p}\MyBestGame" />

<Properties src_game="${src}\${game}" trg_game="${trg}\${game}" pak_game="${pak_root}\${game}" />

<CopyJob>
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cgf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cga" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.mtl" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.dds" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.1" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.2" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.3" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.4" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.5" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.6" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.7" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.8" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.9" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.1a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.2a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.3a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.4a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.5a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.6a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.7a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.8a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.9a" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.adb" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.animevents" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.anm" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.bnk" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.bspace" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.caf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cax" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cdf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.chr" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.chrparams" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.cfg" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.comb" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.dba" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.ent" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.lua" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.skin" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.swf" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.thread_config" copyonly="1" />
    <Job sourceroot="${src_game}\languages" targetroot="${pak_game}\languages" input="*.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_01" targetroot="${pak_game}\Levels\MyBestLevel_01" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_02" targetroot="${pak_game}\Levels\MyBestLevel_02" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_03" targetroot="${pak_game}\Levels\MyBestLevel_02" input="level.pak" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_01" targetroot="${pak_game}\Levels\MyBestLevel_01" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_02" targetroot="${pak_game}\Levels\MyBestLevel_02" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}\Levels\MyBestLevel_03" targetroot="${pak_game}\Levels\MyBestLevel_03" input="filelist.xml" copyonly="1" />
    <Job sourceroot="${src_game}" targetroot="${trg_game}" input="*.xml" copyonly="1" />
</CopyJob>

<PakJob>
    <Job sourceroot="${trg_game}" input="Animations\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Audio\*.*" zip="${pak_game}\MyBestGame_Audio.pak" />
    <Job sourceroot="${trg_game}" input="config\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="difficulty\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="entities\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="libs\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="materials\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="prefabs\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="_newpreview_.mtl" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="autoexec.cfg" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="entities.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="hud_settings.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Savegames.xml" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Objects\*.*" zip="${pak_game}\MyBestGame_Objects.pak" />
    <Job sourceroot="${trg_game}" input="Scripts\*.*" zip="${pak_game}\MyBestGame.pak" />
    <Job sourceroot="${trg_game}" input="Textures\*.*" zip="${pak_game}\MyBestGame_Textures.pak" />
</PakJob>

<Run Job="CopyJob"/>
<Run Job="PakJob"/>

</RCJobs>

 

Запуск скрипта = запуск запаковки.

Всё готово к запаковке:

  • rcjob_PC.xml все действия и пути прописаны.
  • PC.bat — скрипт готов к запуску.

 

После запуска PC.bat и успешной отработки rc.exe по описанным командам в файле rcjob_PC.xml:

Это содержимое каталога D:\MyBestGameBuild\pc\MyBestGame

Если что-то не так (не то скопировано и упаковано, упаковка прервалась и подобное), то необходимо ознакомиться с выводом работы rc.exe в виде текстового файла pclog.log. Ответ обычно в самом конце на моменте прерывания операции.

Таким образом можно упаковать почти всю игру целиком, включая содержимое каталога Bin, кроме шейдерного кэша, который располагается в корневой директории игры в каталоге engine: <Root MyBestGame>\engine\

ShaderCache.pak, ShaderCacheStartup.pak, shaders.pak, ShadersBin.pakэто pak-архивы с шейдерным кэшем и шейдерами, которые запаковываются особым образом и про это будет в пункте про компиляцию шейдерного кэша. Для нетерпеливых:

http://docs.cryengine.com/display/CEPROG/Shader+Cache

http://noostyche.ru/blog/2017/10/21/optimization-shadercache-generation-cryengine-5/

По первому методу упаковки с помощью набора команд для rc.exe на этом всё. Напомню, что это основной и самый правильный метод для CRYENGINE 5.3 и младше. Далее будет рассмотрен альтернативный метод, который имеет право на существование, как метод решения задачи в лоб.


 

2. Запаковка вручную с использованием Save Level Resources.

Подготовка ресурсов.

Примечание: Это альтернативный и не оптимальный крайний метод, подходящий для захламлённого рабочего проекта. Вместо него лучше использовать запаковку с помощью rc.exe.

Save Level Resourcesэто инструмент редактора, который позволяет сохранить в выбранный каталог все используемые на уровне ресурсы, в том числе те, что были в pak-архивах.

Чтобы сохранить все ресурсы, которые применяются на уровнях игры, необходимо загрузить каждый уровень и на каждом выбрать Level / Save Level Resources и сохранить в один и тот же каталог. Каталог выбираем любой, но не каталог рабочего проекта, имя должно быть без спецсимволов и кириллицы, а так же желательно расположение ближе к корню диска, что избавит от проблем из-за ограничения количества символов для путей в Windows. Например:
D:\MyBestGameBuild\SavedLevelResources\

Save Level Resources может сохранить файлы с расширением .cryasset, которые нужны для редактора, а в сборке игры их быть не должно.

Save Level Resources может сохранить часть ресурсов неправильно, закинув их в сторонние подкаталоги, к примеру, сохранить отдельно каталоги с файлами уровней. Так же может быть сохранено лишнее, поэтому необходимо обязательно проверить то, что там насохранялось.

Если используется контент из GameSDK, то лучше сразу распаковать всё, что необходимо для использования в проекте, при том, чтобы было как можно меньше лишнего. Это в первую очередь очень важно для графического контента, который формирует основной «вес» конечной сборки. В то же время gamedata.pak и scripts.pak лучше не распаковывать до последнего, так как это может создать сложности при переходе на новую версию движка. В конечном итоге вытаскивание из GameSDK только нужного, а не всего подряд, очень сильно облегчит процесс сборки и уменьшит занимаемый объём в файловой системе.

Ресурсы из катологов Scripts и Libs в основном нужно докидывать вручную. Если используются ресурсы GameSDK, то необходимо скопировать всё необходимое из них по конфигам и скриптам.

Примечание: В GameSDK каталог Libs находится в gamedata.pak.

Если нет понимания о том, что может понадобиться из тех каталогов, то их можно скопировать в сборку целиком, места они всё равно занимают мало. Исключением может быть каталог Libs/UI, если используется собственное меню и прочие интерфейсы, то из SDK подобное не потребуется.

Пример того, как должно выглядеть в нормальном почищенном виде, останется докинуть каталоги levels и languages (с pak-архивами локализации):

Примечание: Исключением может быть каталог engineassets, так как по-хорошему этот каталог должен располагаться только в engine.pak и дублировать его нет особого смысла.

Важным нюансом использования ресурсов GameSDK заключается в том, что текстуры расщеплены на отдельные mipуровни, представленные отдельными файлами с расширением .dds.1, .dds.2 и так далее. В итоге для одной текстуры получается набор из нескольких файлов, например: trunk_diff.dds, trunk_diff.dds.1, trunk_diff.dds.2 и так далее.

Само расщепление это особая оптимизация, которая применяется в движке для textures streaming.

Проблема расщепления заключается в том, что Save Level Resources не сохраняет файлы с этим расширением, сохраняя только одинокий файл с расширением .dds без его mip-уровней. Это приводит к тому, что текстуры отображаются некорректно в сборке игры. В случае запаковки вручную проблема решается докидыванием недостающих файлов, кто бы мог подумать, вручную.

Докинули недостающее, почистили от файлов .cryasset и привели к подобному виду:

По подготовке ресурсов всё.

 

Непосредственная запаковка.

Запаковывать нужно таким образом, чтобы каталоги с контентом были в корне pak-архива.

Для запаковки рекомендую использовать свободный архиватор 7-zip.

Каталог levels и его подкаталоги с файлами уровней (filelist.xml и level.pak) запаковывать в какой-то ещё pak-архив НЕ НУЖНО, так как вложенные pak-архивы не будут работать.

Каталог languages, содержащий pak-архивы локализации, тоже запаковывать в pak-архив НЕ НУЖНО по той же причине.

Если размер ресурсов превышает 2 Гб, то их нельзя упаковывать в один pak-архив, так как движок не сможет с ним работать. В этом случае придётся сделать несколько pak-архивов.

Сам процесс довольно простой:

0. Выделяем каталоги, которые нужно запаковать, и выбираем в 7z «Добавить к архиву». В данном примере выбраны config, difficulty, Entities, Libs и Materials.

1. Указываем желаемое название для архива и расширение pak. В данном случае pak-архив назван на манер того, как это было в SDK. Кириллицу, пробелы и спецсимволы использовать НЕ НУЖНО.

2. Формат архива: zip.

3. Уровень сжатия по вкусу. Если игра долго запускается, то выбрать меньшее сжатие и наоборот.

4. Метод сжатия: Deflate (другие движок не понимает).

5. Размер словаря стандартный.

6. Размер слова стандартный.

7. Число потоков под количество потоков у процессора.

8. Подтверждаем запаковку.

Результат:

В данном примере объём занимаемого контентом места на жёстком диске снизился почти в два раза.

Руководствуясь тем же принципом, запаковываем остальное, кроме каталогов levels и languages (по ранее описанным причинам), и получаем схожий результат:

В данном примере можно заметить несколько pak-архивов, связанных с Objects. Каталог Objects это основной (корневой) каталог, в котором движок ищет 3D модели. Чтобы движок мог корректно считывать содержимое pak-архива, иерархия каталогов в pak-архиве должна быть такой же, как в распакованном виде:

В примере видно, что внутри ObjectsPropsA.pak содержится каталог Objects, в котором находятся каталоги props и thecursedforest, в которых находятся прочие подкаталоги. Таким образом мы отделили в отдельный pak-архив часть каталогов из Objects, тем самым снизив объём до менее, чем 2 Гб. Таким же образом нужно отделять все каталоги, которые создают превышение лимита в 2 Гб на pak-архив.

Если все ресурсы занимают менее 2 Гб, то их можно запаковать в один pak-архив:

На этом запаковка ресурсов вторым методом заканчивается, но есть ещё пара нюансов по каталогу engine:

engine.pak содержит различные конфиги, специальные текстуры и так далее. Можно почистить от лишнего и перепаковать.

Про настройке конфигов в engine.pak можно почитать здесь: http://noostyche.ru/blog/2017/12/04/configure-configuration-file-graphics-levels-cryengine-5/

ShaderCache.pak, ShaderCacheStartup.pak, shaders.pak, ShadersBin.pakэто pak-архивы с шейдерным кэшем и шейдерами, которые запаковываются особым образом и про это будет в пункте про компиляцию шейдерного кэша. Для нетерпеливых:

http://docs.cryengine.com/display/CEPROG/Shader+Cache

http://noostyche.ru/blog/2017/10/21/optimization-shadercache-generation-cryengine-5/


3. Комбинированный метод запаковки.

Совместить способ запаковки с помощь скрипта rcjob.xml для rc.exe с ручной запаковкой с помощью Save Level Resources. Для этого достаточно будет сохранить используемые ресурсы с применением Save Level Resources, почистить от лишнего и докинуть недостающее, а потом применить rcjob.xml. Метод подойдёт для захламлённого проекта.

На этом завершаем разбор методов запаковки ресурсов игры. В третьей (заключительной) статье будет описано следующее: сборка библиотек и GameLauncher; зачистка библиотек и прочих ресурсов редактора (Sandbox.exe); генерация шейдерного кэша; шифрование запакованных ресурсов; предзагрузка ресурсов.