Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 22 из 25 ~18 мин +50 XP

Сборка: десктоп, мобильные, WASM

Ebitengine сборка из одной кодовой базы даёт четыре цели: десктоп через go build, Android и iOS через gomobile, браузер через GOOS=js GOARCH=wasm — и платформенность почти не доходит до вашего кода. Разберём команды каждого маршрута, эмбед ассетов и подводные камни, которые проявляются только у игрока, а не у автора.

Содержание урока · 10 разделов

Пока игра живёт на вашей машине, вопрос «как её запустить у игрока» кажется теоретическим. Практика наступает в день первой публикации: у кого-то Windows, у кого-то MacBook на Apple Silicon, кто-то откроет ссылку с телефона, а друг потребует браузерную версию без установки. Ebitengine закрывает все четыре цели из одного и того же исходного кода: десктоп собирается go build, мобильные платформы — gomobile, браузер — компиляцией в WebAssembly. Кода под каждую платформу почти нет: платформенность прячется внутри движка, и в ваших файлах не появляются ветки «если Windows, то иначе». Автору остаётся освоить команды сборки, знать требования каждой цели и не наступить на грабли, о которых при разработке не подозреваешь: MIME-тип сервера, dev-пакеты Linux, политика звука iOS. Все четыре маршрута разделяют один Update и один Draw — различия платформ живут в командах сборки, а не в коде игры. В этом уроке маршрут проходит целиком — от первой команды до чек-листа CI, который собирает игру сразу везде. И полезная рамка на весь урок: команды сборки — это код проекта в самом буквальном смысле, они живут в репозитории рядом с исходниками и проверяются тем же способом — запуском.

Десктоп: go build и графические библиотеки#

Десктоп — самый короткий маршрут: команда go build собирает исполняемый файл текущей платформы, и игре больше ничего не нужно. Кросс-сборка задаётся переменными GOOS и GOARCH: с Linux-машины собирается exe для Windows, с Mac — бинарник для Linux, и C-тулчейн для этого не нужен, потому что на Windows и macOS движку не требуются C-зависимости. Исключение по окружению — Linux: перед первой сборкой понадобятся dev-пакеты OpenGL и X11, вроде libgl1-mesa-dev и libx11-dev, — это заголовки для компиляции, а не то, что должен ставить игрок: раздавать можно уже собранный файл. Под капотом Ebitengine сама выбирает графическую библиотеку: DirectX на Windows, Metal на macOS и iOS, OpenGL или WebGL в остальных случаях; выбор можно форсировать переменной EBITENGINE_GRAPHICS_LIBRARY, если конкретный драйвер ведёт себя странно. Проверка десктопного маршрута честная: собранный файл стоит запустить на чистой машине или у друга — ничего, кроме бинарника, с собой тащить не нужно. Подводный камень macOS — архитектура: Intel и Apple Silicon это разные значения GOARCH, и релиз собирается в двух экземплярах. WASM игры Go собираются той же парой переменных, о ней ниже.

Команды десктопной сборки

GO
# текущая платформа — просто go build
go build -o mygame .

# кросс-сборка: цель задаётся GOOS и GOARCH
GOOS=windows GOARCH=amd64 go build -o mygame.exe .
GOOS=darwin GOARCH=arm64 go build -o mygame .

# графическую библиотеку можно выбрать явно
EBITENGINE_GRAPHICS_LIBRARY=opengl ./mygame

Первая команда — сборка под то, на чём вы сидите; вторая и третья — кросс-сборка без C-тулчейна: цель целиком описывается парой GOOS и GOARCH, и один и тот же исходник даёт разные исполняемые файлы. Суффикс .exe важен для Windows-цели. Переменная EBITENGINE_GRAPHICS_LIBRARY переключает бэкенд — DirectX, Metal или OpenGL — без пересборки.

С Linux-машины нужен бинарник для Windows x86-64. Какая команда верна?

Android и iOS: gomobile build и bind#

Мобильные цели собираются через gomobile — официальный инструмент Go для Android и iOS. Установка короткая: go install golang.org/x/mobile/cmd/gomobile@latest, затем gomobile init. Дальше два маршрута. gomobile build упаковывает игру целиком: APK для Android или бандл для iOS — быстрый путь для теста на телефоне и раздачи вне магазинов. gomobile bind собирает библиотеку — aar для Android или xcframework для iOS, — которую встраивают в нативный проект; это путь для релиза в магазинах, где нативный код ведёт рекламу, покупки и экраны публикации. Инфраструктура тяжёлая, но ставится один раз: JDK и Android SDK, Xcode для iOS. Обе утилиты работают поверх обычного кода: пакеты, тесты и структура из прошлого урока не меняются ни на строчку. Особенность исполнения: RunGame обязан стартовать на главном потоке интерфейса, и на мобильных цикл ведёт обвязка пакета ebiten/v2/mobile — ваши Update и Draw вызываются как обычно, просто их запуск устроен хитрее. Проверяйте управление на телефоне заранее: тач-ввод — единственный ввод платформы, и клавиатурные схемы на нём мертвы. Отдельно держите в уме запуск с телефона: подключите устройство, соберите пакет и посмотрите на реальную частоту кадров — жесты и размеры экрана сбивают предположения, сделанные за десктопом.

Браузер: GOOS=js GOARCH=wasm#

Браузерная версия собирается компилятором Go в WebAssembly: GOOS=js GOARCH=wasm go build — и на выходе файл game.wasm. Открыть его двойным щелчком нельзя: модулю нужна HTML-страница и обвязка wasm_exec.js из каталога GOROOT/lib/wasm, которая соединяет код с браузерным API. Страница тривиальная: канвас, тег script с обвязкой и десяток строк вызова — она пишется один раз и живёт во всех проектах курса без изменений. Дальше — веб-сервер, и здесь главный подводный камень маршрута: game.wasm обязан отдаваться с Content-Type application/wasm. С текстовым MIME браузер откажется инстанцировать модуль, и игра не запустится, хотя файлы лежат правильно и пути не битые. Браузеру нужен WebGL 2, а WASM-бинарник весит несколько мегабайт: рантайм Go и движок едут вместе с игрой. Это лечится сжатием на сервере — gzip или brotli ужимает модуль в разы, и после распаковки игрок ждёт секунды, а не минуты. Размер проверяется локально одним взглядом на файл: если модуль растёт с каждым релизом, посмотрите, что нового появилось в вшитых ассетах. Зато обновление игры — перезагрузка страницы: никакой установки, согласований и подписей, поэтому браузерная версия становится витриной проекта раньше всех остальных сборок.

Сборка и раздача WASM

GO
# компиляция модуля для браузера
GOOS=js GOARCH=wasm go build -o build/web/game.wasm .

# обвязка syscall/js из установки Go
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" build/web/

# index.html подключает wasm_exec.js и грузит game.wasm;
# сервер обязан отдавать правильный Content-Type:
w.Header().Set("Content-Type", "application/wasm")

Три шага маршрута: компиляция под js/wasm, копирование обвязки из GOROOT и раздача с правильным MIME-типом. Страница подключает wasm_exec.js, инстанцирует модуль и запускает игру; без заголовка application/wasm браузер отвергнет файл ещё до выполнения первой строки кода. Проверить маршрут проще всего локальным сервером из пары строк Go.

Игра не запускается в браузере — что случилось?

Прочитайте код и выберите, что он выведет:

# файлы на месте, пути в index.html верные:
# build/web/game.wasm, build/web/wasm_exec.js
# но сервер отдаёт статике с MIME-типом text/plain
#
# в консоли браузера: instantiateStreaming failed

Эмбед ассетов: ресурсы внутри бинарника#

Ассеты — тихая причина сломанной сборки. Пока разработка идёт в папке проекта, чтение файлов с диска работает; но собранный бинарник запускают не из исходников, и папка sprites может остаться дома. Решение — директива //go:embed: компилятор вшивает файлы в бинарник, и они доступны через embed.FS в любом месте кода. Ассетам отводится пакет internal/assets из прошлого урока: там объявляется переменная FS с перечислением папок, и весь остальной код берёт ресурсы только через неё. Выигрыш не только в удобстве: эмбед работает одинаково на десктопе, в мобильном бандле и в WASM-модуле, потому что байты лежат внутри исполняемого файла, а не рядом с ним. Спрайты из урока про спрайты и звуки из урока про звук читаются из FS без изменений игрового кода. Важно правило одного источника: чтение с диска в пакете с эмбедом смешивать нельзя — иначе половина ассетов поедет в бинарнике, а половина останется дома, и упадёт только собранная версия. Ограничение одно: паттерн не может выходить за каталог пакета — папки обязаны лежать внутри internal/assets, что естественно совпадает со скелетом проекта. Плата — размер: вшитые ресурсы увеличивают бинарник на своих мегабайт, и для WASM это надо помнить при выборе набора файлов.

Пакет ассетов с эмбедом

GO
// internal/assets/assets.go
package assets

import "embed"

//go:embed sprites sounds fonts
var FS embed.FS

// спрайт: байты из FS грузятся в ebiten.Image,
// звук: те же байты -> NewPlayerF32FromBytes
// из урока про звук — один источник для всех платформ

Директива перечисляет папки, компилятор вшивает их содержимое, а переменная FS становится единственным входом к ресурсам. Игровой код не знает, откуда взялись байты, — на десктопе, телефоне и в браузере это одни и те же значения, и папки рядом с бинарником больше не нужны. Пропущенная папка в перечислении ловится на сборке: ресурса нет в FS — значит, читаем не оттуда.

Что меняет //go:embed по сравнению с чтением файлов с диска?

Платформа — команда — подводный камень#

ПлатформаКомандаПодводный камень
Windowsgo build, кросс: GOOS=windows GOARCH=amd64не забыть суффикс .exe у выходного файла
macOSGOOS=darwin с GOARCH=amd64 и arm64Intel и Apple Silicon собираются по отдельности
Linuxgo build на местеперед первой сборкой нужны dev-пакеты OpenGL и X11
Android и iOSgomobile build или gomobile bindпервый запуск требует gomobile init, JDK и Xcode
БраузерGOOS=js GOARCH=wasm go buildсервер обязан отдавать application/wasm

Ограничения платформ и CI-матрица#

У каждой цели есть потолок, о котором лучше узнать до релиза, а не после. WASM: мегабайтный бинарник, требование WebGL 2 и медленный первый запуск без сжатия на сервере. Android и iOS: тяжёлая инфраструктура SDK, подписи, и маленькая ловушка звука — на iOS аудио оживает после первого касания, политика автовоспроизведения запрещает играть звук без жеста пользователя, поэтому стартовый экран, где игрок тапает «начать», полезен и для запуска аудиоконтекста. Десктоп: раздача без установщика требует, чтобы бинарнику ничего не понадобилось рядом, — ещё один аргумент за эмбед. Всё это собирает CI-матрица: джоба перебирает пары GOOS и GOARCH, для мобильных гоняет gomobile, для браузера — сборку с обвязкой, и каждая сборка хотя бы запускается на смоук-тест. Достаточный минимум для игр курса — три джобы: десктоп текущей платформы, веб и один мобильный билд; остальное подключается по мере роста проекта. Кэш модулей делает матрицу быстрой, артефакты складываются в релиз одной кнопкой. Полезное правило матрицы: каждая джоба воспроизводима локально — любая строка CI должна запускаться с вашей машины той же командой, иначе отладка красной сборки превращается в угадывание. Итоговый проект курса публикуется именно так — одним пушем, четырьмя артефактами, без ручных повторов. Отдельный плюс матрицы — история: артефакт прошлой сборки остаётся доступным, и regression можно отличить от поломки окружения.

Соберите пакет ассетов

Восстановите два пропуска: пакет стандартной библиотеки и имя переменной.

package assets import "" //go:embed sprites sounds var embed.FS

Шаги CI-джобы сборки

Соберите шаги релизной джобы в рабочий порядок.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Чек-лист сборки#

  • Десктоп: go build, кросс-сборка через GOOS и GOARCH без C-тулчейна
  • Linux: dev-пакеты OpenGL и X11 нужны компилятору, а не игроку
  • Мобильные: gomobile build для готового пакета, gomobile bind для нативного проекта
  • Браузер: GOOS=js GOARCH=wasm плюс wasm_exec.js и HTML-страница
  • Сервер отдаёт game.wasm с Content-Type application/wasm — иначе отказ запуска
  • Ассеты вшиваются //go:embed и едут внутри бинарника на всех платформах
  • iOS: звук оживает после первого касания — экран «начать» обязателен
  • CI-матрица собирает все цели и гоняет смоук-тест каждой сборки

Сборка — мост между кодом и игроком, и проверяется он только проходом: соберите каждую цель руками хотя бы раз, пока проект маленький и правки дёшевы. Домашнее задание проведёт по всем маршрутам, и после него команды перестанут быть заклинаниями: каждая строчка будет отвечать на вопрос, который вы уже решали своими руками. Дальше курс разбирает последнее перед итоговым проектом: где брать ассеты и как не нарушить лицензии, — и папки, которые сегодня вшивались в бинарник, наконец обретут легальный источник. Запомните главное: одна кодовая база — четыре цели, эмбед делает ассеты переносимыми, а правильный Content-Type — половина успеха браузерной версии. Платформы не требуют разветвления кода — только дисциплины сборки. Один и тот же Update работает везде, а различия платформ остаются там, где им место: в командах и настройках серверов.

Термины урока

Термин

GOOS и GOARCH

нажмите, чтобы перевернуть
Определение

Переменные кросс-сборки: целевая операционная система и архитектура. GOOS=windows GOARCH=amd64 собирает exe с любой машины.

1 / 5

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Одна кодовая база — четыре цели: десктоп, Android, iOS и браузер без веток в коде.
  • Десктоп собирается go build; кросс-цель задаётся парой GOOS и GOARCH.
  • На Linux компилятору нужны dev-пакеты OpenGL и X11, игроку — ничего.
  • Мобильные цели идут через gomobile: build для пакета, bind для нативной обвязки.
  • WASM-сборка требует wasm_exec.js, HTML-страницы и Content-Type application/wasm.
  • Эмбед вшивает ассеты в бинарник — ресурсы едут вместе с игрой на всех платформах.
  • CI-матрица перебирает цели, гоняет смоук-тесты и складывает артефакты в релиз.

Домашнее задание

Соберите игру для всех целей урока. Десктоп: go build под текущую платформу и кросс-сборка GOOS=windows GOARCH=amd64 — проверьте, что exe запускается без исходников рядом. Браузер: соберите build/web с game.wasm и wasm_exec.js, напишите минимальный сервер с заголовком application/wasm и убедитесь, что игра стартует и без заголовка — падает. Затем вшейте ассеты: перенесите спрайты и звуки в internal/assets, объявите FS с //go:embed и переведите загрузку на него, после чего удалите папки ресурсов рядом с бинарником — игра обязана работать. Проверьте живое место iOS-маршрута: на старте игры ставьте экран «тап, чтобы начать», заодно запускающий аудиоконтекст. В конце опишите CI-матрицу в виде скрипта из порядка джобы: тест, десктоп, веб, gomobile — и запустите его целиком, зафиксировав время прохождения: если матрица идёт дольше минуты, включите кэш модулей.

Личные заметкиПусто
Хранятся только в вашем браузере (Ebitengine · Сборка: десктоп, мобильные, WASM)

Вопросы и ответы по уроку

Почему WASM-бинарник весит мегабайты?

В модуль вшивается весь рантайм Go плюс графический движок и ваши эмбеднутые ассеты — сборщик не умеет выбрасывать то, что может понадобиться. Практическое лечение — сжатие на сервере: gzip или brotli ужимает файл в разы, а кэш браузера делает повторные визиты почти мгновенными.

Можно ли публиковать на Android без gomobile?

Для магазина — нет: Google Play и App Store принимают пакеты, которые собирает именно gomobile или нативный проект с библиотекой от gomobile bind. Обходной маршрут — браузерная версия: она работает в мобильном браузере без магазина, но лишается полноэкранного ввода и выглядит демо, а не релизом. Третий путь — привлечение соавтора с нативным опытом: библиотека от gomobile bind отдаётся со скелетом вызова, и чужой код дополняет игру, не трогая её.

Почему на iOS звук молчит до первого касания?

Так работает политика автовоспроизведения мобильных платформ: аудиоконтекст оживает только после жеста пользователя. Игровой код не меняется — нужен стартовый экран с тапом «начать», который заодно запускает контекст звука, и музыка из урока про звук стартует сразу после него.

Термины урока

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

Следующая ступень учебного пути

Chromedp25 уроков~11 чпрактика25 уроков уже доступны

Chromedp в Go: автоматизация Chrome без Selenium

Курс chromedp для Go: навигация, селекторы, клики и формы, скриншоты и PDF, JS-оценка, вкладки, прокси, пул браузеров и скрапинг. 25 уроков.

  • Запускать Chrome из Go и управлять страницами
  • Находить элементы селекторами и ждать их появления
  • Кликать, заполнять формы и извлекать данные
  • Делать скриншоты, PDF и выполнять JavaScript