Несколько месяцев назад я возился с написанием Space Invaders на Go. Мне было очень весело писать его, но вывод работал только на iTerm2, потому что он использовал специфическую функцию этого терминала. Чтобы играть в игру, вам действительно нужно скачать iTerm2 и запустить его из командной строки. Я подумал, что было бы забавно еще немного поиграть с ней, чтобы посмотреть, смогу ли я превратить ее в настоящую настольную игру.
Теперь, если вы вообще много знаете о Go, вы должны знать, что просто нет хорошего инструментария или библиотеки с графическим интерфейсом. Go не разрабатывался с учетом пользовательского интерфейса, поэтому в стандартной библиотеке определенно нет пакетов пользовательского интерфейса. Есть обычные привязки GTK и QT, но обычно я стараюсь избегать привязок, если могу. Более популярный способ написания десктопных приложений на Go — это создать веб-приложение, а затем использовать для него браузероподобный интерфейс. Обычный способ сделать это — использовать Электрон, фреймворк, используемый для написания кроссплатформенных настольных приложений с использованием Javascript, HTML и CSS. Многие настольные приложения использовали Electron, включая настольные приложения Slack и Github, и даже редактор, который я использую сейчас (Visual Studio Code).
Тем не менее, Electron довольно тяжелый, использует довольно много Javascript и имеет тонны документации, которую нужно изучить. Я искал что-то ориентированное на Go и простое для запуска. Что еще более важно, я просто хотел использовать то, что уже создал ранее, что представляет собой не что иное, как быстрое отображение серии изображений, чтобы они выглядели должным образом анимированными.
Затем я наткнулся на эту маленькую библиотеку Go под названием webview. Webview — это крошечная и простая библиотека, которая работает с webview (MacOS), MSHTML (Windows) и gtk-webkit2 (Linux). Его документация для части Go занимает всего одну страницу или около того!
Давайте подведем итоги того, что нам нужно сделать:
- Создайте веб-приложение, которое будет обслуживать кадры
- Показать веб-приложение в веб-просмотре
- Внесите изменения в логику игры, чтобы она работала
Это все! Пойдем.
Мы начинаем с создания отдельной горутины для запуска веб-приложения, а затем отображаем статическую HTML-страницу из веб-просмотра.
Посмотрите, как создание веб-просмотра — это всего лишь одна строка кода! Если вы сравните это с Electron, это довольно круто. Конечно, у Electron немного больше возможностей, но для чего-то простого и понятного вы просто не сможете победить его.
Веб-приложение простое и понятное.
Я не использовал ничего необычного, это просто обычное веб-приложение Go с 3 обработчиками. Он также обслуживает html и другие ресурсы через каталог с именем public
. Обработчик start
запускает игру, обработчик frame
возвращает кадр, а обработчик key
получает события клавиатуры.
Обработчик start
начинает генерировать кадры в отдельной горутине, затем вызывает шаблон invaders.html
, передавая ему частоту кадров.
Обработчик captureKeys
получает события клавиатуры из веб-представления и выполняет соответствующие действия. Если игра закончилась, вы можете перезапустить или выйти из игры. В противном случае все события клавиатуры помещаются в канал событий.
Через каждый интервал веб-просмотр будет вызывать getFrame
для кадра. Кадр представляет собой URI данных изображения с изображением в кодировке base64. Затем это передается тегу <img>
в шаблоне HTML (который мы увидим позже). Обратите внимание, что мы установили для заголовка Cache-Control
значение no-cache
. Это обходной путь для MSHTML (особенно в Windows), потому что в противном случае кадр изображения будет кэшироваться, и игра зависнет на первом кадре.
Кадры отображаются на HTML-странице, которая будет отображаться в веб-просмотре. Стартовую страницу не нужно анимировать (или можно анимировать с помощью видеоклипа или гифки), поэтому она может быть полностью статической HTML-страницей.
Он просто захватывает события клавиатуры через JQuery и перенаправляет на обработчик start
при нажатии s
. Это запустит игру.
Как только мы запускаем игру, запускается игровой цикл, и кадры создаются повторно. В игровой цикл введена переменная gameDelay
, чтобы замедлить игру, если она становится слишком быстрой. Отображение кадров сводится к использованию JQuery и получению одного кадра через регулярные промежутки времени.
Для этого я просто использовал setInterval
с частотой, предоставленной обработчиком start
. Функция в setInterval
использует метод Jquery get
для получения URI данных из обработчика frame
и изменяет значение атрибута src
тега <img>
на идентификатор image
.
Я также отслеживаю событие keydown
и использую метод JQuery get
для отправки захваченного значения обработчику key
.
Так выглядит стартовый экран в Windows.
Вот как это выглядит на Mac.
Давайте посмотрим на изменения, которые мне нужно внести в логику игры. Большая часть кода не меняется. Однако игра находится в веб-просмотре, поэтому элементы управления также будут в самом веб-просмотре. Это означает, что мне больше не нужно использовать терминологию. Вместо этого я просто фиксирую события клавиатуры, отправленные в веб-просмотр, используя метод JQuery keydown
, и отправляю их обработчику key
. Обработчик key
, в свою очередь, добавляет его в канал event
(ранее я отправлял в канал событие клавиатуры termbox).
В основном игровом цикле вместо проверки событий клавиатуры терминов я проверяю события клавиатуры из веб-просмотра.
Посмотрите, как я воспроизвожу звук каждый раз, когда обнаруживаю строку 32
(захваченную из события клавиатуры), которая указывает на нажатие пробела.
Воспроизвести звук
Игры лучше работают с игровыми звуками и эффектами. Я получил звуки спецэффектов Space Invaders с сайта Classics United, а также использовал пакет Beep для их воспроизведения.
Воспроизведение звукового эффекта — это просто получение файла WAV, его декодирование и воспроизведение. К сожалению, пакет закрывает файл после его декодирования, поэтому мне приходится каждый раз открывать файл заново, но это работает достаточно хорошо.
Показать счет игры в конце игры
Еще кое-что, что меняется в игре, — это способ отображения очков. Когда игра заканчивалась ранее, я просто показывал счет на терминале. Теперь, когда у меня нет терминала для отображения результатов, лучше всего делать это на экране. Что мне нужно, так это написать текст в конце игры.
Для этого я использовал пакет image/font
в подрепозиториях стандартной библиотеки Go. Напомним, подрепозитории стандартной библиотеки Go — это экспериментальные пакеты, которые находятся в разделе golang.org/x/*
. В частности, пакет golang.org/x/image/font
предоставляет нам базовые возможности для создания строк текста на изображении.
Когда я сказал базовый, я действительно имел в виду базовый. Хотя с его помощью можно делать и более сложные вещи, в итоге я использовал только базовые функции (в первую очередь, чтобы код оставался простым).
Функция printLine
принимает изображение, координаты для написания текста, сам текст и цвет текста, затем рисует строки текста заданного цвета в указанных координатах. Используемый шрифт — готовый из пакета inconsolata
.
Вот как это используется в коде игры.
Чтобы создать приложение на Mac, просто используйте скрипт build-macOS
. Он должен собрать приложение, а затем соответствующим образом поместить его в пакет приложения invaders.app
. При этом вы можете просто дважды щелкнуть приложение и начать играть!
Чтобы собрать приложение в Windows, используйте эту команду:
go build -ldflags="-H windowsgui" -o invaders.exe
После этого у вас должен быть invaders.exe
двоичный исполняемый файл, который вы можете дважды щелкнуть, чтобы начать воспроизведение.
Вы можете найти исходный код здесь.
https://github.com/sausheong/invadersapp
Вот и все! Я не стал читать код игры, потому что уже объяснял его в предыдущем сообщении в блоге.
А пока вот как это выглядит в Windows. Ответ немного шаткий, потому что на самом деле у меня нет компьютера с Windows, я протестировал его и создал видео на виртуальной машине VirtualBox.
Вот как это выглядит на Mac.
Развлекайся!
- Спасибо Ибрагиму Ву, который помог мне отладить приложение в Windows, а также обнаружил проблему с кэшированием MSHTML.
- Спасибо Сергею Зайцеву за его потрясающий пакет webview!
Первоначально опубликовано на https://sausheong.github.io 12 августа 2018 г.