Unreal EngineSeniorSystem design
Как упаковать (package) и развернуть игру на Unreal Engine для разных платформ?
Packaging использует UAT (UnrealAutomationTool) с командой BuildCookRun, которая компилирует код, кукает ассеты в .pak файлы и стейджит результат. Deployment зависит от платформы: Steam, консольные devkits или ручной деплой бинарников.
Packaging и деплой игры на Unreal Engine
Архитектура пайплайна упаковки
Packaging в Unreal состоит из четырёх стадий: Build (компиляция C++), Cook (конвертация ассетов в платформо-специфичные форматы), Stage (копирование в staging directory) и Pak (упаковка в .pak архивы с опциональным шифрованием).
Базовая команда упаковки через UAT
#!/bin/bash
UE_ROOT="/opt/unreal-engine-5.3"
PROJECT="/workspace/MyGame/MyGame.uproject"
OUTPUT="/output/builds"
# Windows (Shipping)
"$UE_ROOT/Engine/Build/BatchFiles/RunUAT.bat" BuildCookRun \
-project="$PROJECT" \
-platform=Win64 \
-clientconfig=Shipping \
-serverconfig=Shipping \
-cook \
-build \
-stage \
-pak \
-archive \
-archivedirectory="$OUTPUT/Win64"
# Linux (Dedicated Server)
"$UE_ROOT/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun \
-project="$PROJECT" \
-platform=Linux \
-clientconfig=Shipping \
-serverconfig=Shipping \
-cook \
-build \
-stage \
-pak \
-server \
-noclient \
-archive \
-archivedirectory="$OUTPUT/LinuxServer"
# Mobile (Android)
"$UE_ROOT/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun \
-project="$PROJECT" \
-platform=Android \
-clientconfig=Shipping \
-cookflavor=ETC2 \
-cook -build -stage -pak -archive \
-archivedirectory="$OUTPUT/Android"
Конфигурация таргетов
# MyGame/Source/MyGame.Target.cs — контролирует тип сборки
# MyGame/Source/MyGameServer.Target.cs — серверный таргет
# Пример Target.cs для Shipping:
# using UnrealBuildTool;
# public class MyGameTarget : TargetRules
# {
# public MyGameTarget(TargetInfo Target) : base(Target)
# {
# Type = TargetType.Game;
# DefaultBuildSettings = BuildSettingsVersion.V5;
# bUsesSteam = true;
# }
# }
Шифрование pak-файлов
# Config/DefaultCrypto.json
# {
# "EncryptionKey": {
# "Name": "MyGame",
# "Guid": "00000000-0000-0000-0000-000000000000",
# "Key": "base64-encoded-32-byte-aes-key-here"
# },
# "bEnablePakSigning": true,
# "bEnablePakIndexEncryption": true,
# "bEnablePakEntryEncryption": true
# }
# Генерация ключа:
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64
Деплой в Steam
# steamworks_sdk/tools/ContentBuilder/builder_linux/steamcmd
cat > /tmp/steam_build.vdf <<'EOF'
"AppBuild"
{
"AppID" "1234567"
"Desc" "CI build $(git rev-parse --short HEAD)"
"BuildOutput" "/output/steam"
"Depots"
{
"1234568"
{
"FileMapping"
{
"LocalPath" "/output/builds/Win64/WindowsNoEditor/*"
"DepotPath" "."
"recursive" "1"
}
}
}
}
EOF
steamcmd \
+login "$STEAM_USERNAME" "$STEAM_PASSWORD" \
+run_app_build /tmp/steam_build.vdf \
+quit
Деплой dedicated server (Linux)
# Загружаем на сервер через rsync
rsync -avz --delete \
/output/builds/LinuxServer/MyGameServer/ \
deploy@gameserver:/opt/mygame/
# Systemd unit для автозапуска
sudo tee /etc/systemd/system/mygame.service <<'EOF'
[Unit]
Description=MyGame Dedicated Server
After=network.target
[Service]
User=gameserver
WorkingDirectory=/opt/mygame
ExecStart=/opt/mygame/MyGameServer-Linux-Shipping \
/Game/Maps/MainMap \
-port=7777 \
-QueryPort=27015 \
-log \
-NOSTEAM
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now mygame
CI/CD пайплайн (GitHub Actions)
name: Package and Deploy
on:
push:
branches: [release/*]
jobs:
package:
runs-on: [self-hosted, linux, unreal]
steps:
- uses: actions/checkout@v4
with:
lfs: true
- name: Cook and Package
run: |
/opt/ue5.3/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \
-project=$GITHUB_WORKSPACE/MyGame.uproject \
-platform=Linux \
-clientconfig=Shipping \
-cook -build -stage -pak -archive \
-archivedirectory=/output/${{ github.sha }}
- name: Deploy to Staging
run: |
rsync -avz /output/${{ github.sha }}/ \
deploy@staging:/opt/mygame/
ssh deploy@staging 'sudo systemctl restart mygame'
Подводные камни
- Cook без общего DDC на CI занимает часы — настройте shared filesystem DDC или S3-compatible backend через
UE-SharedDataCachePathenv variable. -cookflavorдля Android должен включать ETC2 и ASTC — без этого на части устройств будут чёрные текстуры.- Shipping build по умолчанию включает
bSplitIntoChunksдля DLC — без явного отключения создаются лишние .pak файлы. - Ключ шифрования pak нельзя менять после релиза без патча клиента — храните его в секрете с самого начала (CI secrets, не в репозитории).
- Linux dedicated server требует точное совпадение версии libc между build-машиной и prod-сервером — используйте Docker для сборки.
- Steam upload занимает время даже если контент не изменился — всегда указывайте
setliveотдельным шагом после верификации билда. - Консольные платформы (PS5/Xbox) требуют отдельного NDA, SDK и certified devkit — packaging pipeline значительно отличается от PC/Linux.
- Версия
EngineAssociationв.uprojectдолжна точно совпадать с установленной версией на build-агентах — иначе UAT завершится с ошибкой поиска Engine.
Common mistakes
- Объяснять packaging и deployment только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Использовать макросы или specifier-и наугад и забывать про UObject GC.
What the interviewer is testing
- Кандидат формулирует точную модель для packaging и deployment, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.
- Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.