Несколько месяцев назад я возился с написанием 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 занимает всего одну страницу или около того!

Давайте подведем итоги того, что нам нужно сделать:

  1. Создайте веб-приложение, которое будет обслуживать кадры
  2. Показать веб-приложение в веб-просмотре
  3. Внесите изменения в логику игры, чтобы она работала

Это все! Пойдем.

Мы начинаем с создания отдельной горутины для запуска веб-приложения, а затем отображаем статическую 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 г.