Как мы разрабатываем игры для Pixel Quest, публикуем исходники и делимся частью выручки

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Полгода назад я писал про наш путь к открытию первой пиксельной игровой Pixel Quest: путь от прототипа до первого игрового заведения. Сегодня хочу рассказать, как мы решили проблему разработки игр путём перехода с Go на Lua и публикации исходников, а также о планах делиться частью выручки со сторонними разработчиками.

Проблематика и поиск решения

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

Нужна была удобная платформа для разработки, а т.к Go – язык компилируемый, то было сложно сделать стенд для тестирования кода, не предоставляя доступ к исходникам игрового контроллера. Поиск решения привёл меня к идее использовать скриптовый язык Lua. 

Как оказалось, язык Lua – очень простотой в изучении и использовании, но в то же время один из самых быстрых и производительных скриптовых языков программирования! Де-факто он является стандартом в игровой индустрии и используется, например, в таких играх, как World of Warcraft, Roblox, Minecraft и Angry Birds.

Для интерпретации Lua скриптов на Go есть неплохая виртуальная машина GopherLua. По производительности она сравнима с Python версией, но, конечно, уступает нативной реализации на C. Но нам не космолёт строить, логика игр часто весьма простая, и в крайнем случае сложные вычисления можно будет вынести обратно на сторону Go.

Интегрировать машину в существующий Go код оказалось весьма просто и заняло буквально пару дней. Она позволяет как выполнять Lua код из Go, так и вызывать Go код из Lua. Я был приятно поражён гибкостью такого решения и очень пожалел, что откладывал момент интеграции так долго.

Пример кода вызова Lua функции из Go
func (g *GameType) NextTick() (*base_game.GameResultsType, error) {
	if g.OnPause() {
		return nil, nil
	}

	err := g.L.CallByParam(lua.P{
		Fn:      g.nextTickFn,
		NRet:    1, // number of returned values
		Protect: true,
	})
	if err != nil {
		return nil, fmt.Errorf("NextTick failed to call LUA func: %v", err)
	}

	ret := g.L.Get(-1) // returned value
	g.L.Pop(1)         // remove received value
	if ret == lua.LNil {
		return nil, nil
	}

	tblResult, ok := ret.(*lua.LTable)
	if !ok {
		return nil, fmt.Errorf("NextTick failed to cast LTable: %v", err)
	}

	var result base_game.GameResultsType
	err = gluamapper.Map(tblResult, &result)
	if err != nil {
		return nil, fmt.Errorf("NextTick failed to map table: %v", err)
	}

	// Донавесим GameDurationSec
	gameStats, err := g.BaseGameType.GetStats()
	result.GameDurationSec = gameStats.GameDurationSec

	return &result, err
}

Структура игры

Для управления игрой я выделил 11 обязательных методов (я ни разу не игродел, соответственно буду рад обсудить ваши варианты):

  • StartGame(gameJson, configJson) – старт игры. Тут декодируем игровой и конфиг json для инициализации игры и её стартового состояния;

  • NextTick() – тик игрового мира, здесь обычно описана вся основная логика. В текущих реалиях вызывается ПРИМЕРНО каждые ~35мс (28 кадров в секунду), но ориентироваться на время периода нельзя, вместо этого нужно использовать абсолютное время. Не вызывается, когда игра на паузе или завершена;

  • RangeFloor(setPixel, setButton) – опрос снапшота пола, вызывается в тот же игровой тик следом за NextTick(), чтобы забрать состояние пола для отрисовки;

  • GetStats() – забор статистики игры, вызывается сразу за RangeFloor() для получения информации для отрисовки игрового табло;

  • PauseGame() – событие паузы игры;

  • ResumeGame() – событие снятия игры с паузы;

  • SwitchStage() – дополнительный рычаг админу для переключения этапа, может быть полезен в некоторых играх;

  • PixelClick(click) – событие клика/отпускания пикселя;

  • ButtonClick(click) – событие клика/отпускания кнопки;

  • DefectPixel(defect) – событие дефектовки/раздефектовки пикселя;

  • DefectButton(defect) – событие дефектовки/радефектовки кнопки;

Оффтоп про дефектовку

Нас тут коллеги из Испании спрашивали: а как вы делаете так, чтобы не отправлять людей домой в случаях, когда что-то сломалось?

Отвечаем: у нас есть механизм дефектовки – это когда пиксель/кнопка, не реагирующие на нажатие, выводятся из игры и не блокируют игровой процесс. Чаще всего цвета они отображать могут, а нажиматься – уже нет. Таким образом, ни в одной игре на такие пиксели или кнопки больше не будет назначено задание и игроки в большинстве своём даже не замечают, что с полом что-то не так.
Иногда картина выглядит, прямо скажем, очень печально, особенно после нагруженных выходных дней, когда времени на ремонт/замены совсем нет:

Куча поломок после праздников 8 марта
Куча поломок после праздников 8 марта

Но мы работаем над качеством: в новой версии весовые датчики будут вдвое мощнее, а кнопки станут емкостными без физического нажатия.

После определения основных методов управления игрой мы переписали несколько наших ранее реализованных простых игр с Go на Lua. Исходный код можно посмотреть в репозитории github.com/pixel-quest/pixel-games, там же есть идеи некоторых игр в очереди на разработку + скромная Wiki с документацией.

Жизнеспособность идеи была подтверждена, игры работали как надо. И даже управление аудиоплеером удалось прокинуть в Lua без особых проблем. Оставалось дело за малым – удобной платформой для тестирования скриптов.

Платформа для разработки

Хотелось иметь онлайн платформу, в которой можно писать код игры и тут же запускать его на виртуальном пиксельном полу для тестирования. За основу был взят наш существующий админ-интерфейс, написанный на Vue. Я его сильно урезал в части функционала управления электроникой, оставил только виртуальный пол и прикрутил навороченный текстовый редактор в виде VS Code. 

Наша онлайн IDE платформа
Наша онлайн IDE платформа

Для обеспечения прозрачного механизма отладки я сделал на вебсокетах проброс ошибок в виде всплывающих нотификаций и добавил возможность вывода в Lua коде дебаг сообщений log.print() прямо в консоль разработчика в браузере. Также есть валидация json при сохранении конфигов. 

Пример ошибки и вывод дебаг сообщения
Пример ошибки и вывод дебаг сообщения

Пока что поддерживается работа только с одним набором файлов (скрипт, игровой и конфиг json'ы) и нет истории изменений, так что надо быть очень аккуратным при сохранении или запуске игры. Эти доработки есть в планах, а пока что тестируем саму концепцию.

Пинг-понг на виртуальном полу, исходники есть в репозитории
Пинг-понг на виртуальном полу, исходники есть в репозитории

Так как я опасаюсь лавинной нагрузки, с которой может не справиться мой говнокод дешевый VPS (@ru_vds, может накините пару ядер на халяву?

Источник: https://habr.com/ru/articles/799205/


Интересные статьи

Интересные статьи

В мире есть много способов превратить процесс изучения программирования в увлекательное занятие. Один из них — игры. Мы отобрали несколько лучших вариантов, которые позволят не только развлечься, но и...
В настольной игре "Не закороти Цепь! Последовательная история" относительно несложная механика. Батарейка, провода, светодиоды и лампочки — собирай замкнутую цепь от плюса к минусу, смотри, что зажгло...
Прежде чем приступать к написанию кода, нам нужно решить, что именно такого полезного мы можем создать, какие функции будет выполнять библиотека. Довольно популярным является написание библиотек, упро...
Доброго времени суток, друзья! Хочу поделиться с вами опытом разработки простого чата на React с помощью библиотеки «Socket.IO». Предполагается, что вы знакомы с названной библи...
«Я знаю, что ничего не знаю» Сократ Для кого: для IT-шников, которые плевали на всех разработчиков и хотят поиграть в свои игры! О чем: о том, как начать писать игры на C/C++, если вд...