Многие из нас играли в железную дорогу в детстве и мечтали о втором-третьем наборе рельс, чтобы построить свою ветку от балкона до прихожей. Нашей команде удалось поучаствовать в виртуальном строительстве огромного транссиба с развязками поражающими воображение.
Кстати, на сайте Канобу еще неделю будет длиться конкурс с билетами на Игромир в качестве приза.
Ссылка на игру
и на внеконкурсную версию (без обертки Канобу)
Сюжет игры — полная противоположность subway surfer, надо защищать поезд от набегов идиотов. Поскольку это официальная игра, из поезда не может выйти охранник и навешать люлей. Поезд не может попасть в аварию или даже случайно кого-то задавить. В процессе было много заманчивых идей, возможно когда-то свет увидит версию с поездом, изничтожающим толпы зомби.
Эта статья не является hello-world для HTML5-игры в стиле crappy bird. Мы говорим о реальном проекте с точки зрения разработчика, о кране сжатых сроках, о написании игры с кучей хаков, о нервировании огромной цепочки заказчиков сдачей проекта в последний день, без запаса по времени. Хотите чистого кода — идите на github и мучайте наше детище сколько хотите. Код лежит под лицензией MIT, но весь арт сюда не входит.
Как РЖД печётся о безопасности мы знаем по огромным экранам на вокзалах. Лично я думаю, что если бы эти ролики не были зацензурены, и были выполнены в стиле mortal kombat, эффект был бы гораздо больший (Poezd wins. Fatality).
Так вот, где-то в РЖД решили что неплохо бы сделать небольшое промо с упором на безопасность на железнодорожных путях. Сложная цепочка посредников в итоге привела к нам
ТЗ был расписан достаточно прямо: должен быть генерируемый уровень, вид «сверху-сбоку», поезд соответствующий стилю РЖД, 6 видов препятствий и 3 супер героя, объясняющих как не надо себя вести на путях. Всё это должно работать на адекватных браузерах, на PC и планшетах. В качестве супер героев были выбраны Супермен, Человек-Паука и «тот парень из из GTA San Andreas»…
Думаете мы использовали свой супер-движок, который может рендерить всё на DIV либо на CANVAS2D либо SVG и прорисовывающем только те места где что-то менялось? Думаете мы взяли свою super-CMS для генерирования страниц и менеджмента скриптов? Нет, мы взяли самый лёгкий рендерер на свете — pixi.js, самый лёгкий на данный момент сборщик скриптов — gulp+browserify, самую простую библиотеку для single-app — angular.js, самый тупой бэкэнд — node.js, самую тупую нарезалку спрайтов — shoebox, самый простой в использовании 3d-редактор — blender, самый тупой редактор многоугольников — physicseditor, самый лучший скелетный 2d-animator spine. Инфраструктурых вопросов у нас не возникало. Мы вообще технически не выделялись.
Был составлен халявный план:
0) поезд будет проезжать целый экран, и останавливаться на станции, пока генерируется следующий экран.
1) надо чтобы был путь с фиксированной кривизной на поворотах, который можно удлинять и у которого можно получать координаты шпал.
2) путь надо уметь проверять на самопересечения. Используем простую сетку, храним в каждой клетке кто её пересекает. Мельче сетка — больше вероятность что пути начнут пересекаться.
3) путь должен заканчиваться где-то справа экрана, чтобы его можно было продолжать при генерации следующего экрана.
4) должен быть тупой алгоритм который перебирает такие пути и выбирает покрасивее и подлиннее.
5) результат можно заполнить разными объектами (деревьями и камушками).
6) поезд можно зарендерить в 32 кадра. Выглядит это не очень, но «доворот» спрайта до нужного угла прямо в игре всё решает. Это удалось сделать довольно быстро, соотношение результат/время было офигенным, на демку можно было медитировать:
первая демка
Работа сильно опережала график, и генерацию пути можно было бы всё так и оставить, но кто-то в середине цепочки решил поиграть в серьёзного заказчика, и заявил, что нужен другой вариант. С плавным скроллингом. Испорченный телефон добавил что-то про стрелки и дополнительные пути, и это всё привело к трём неделям дебага, оптимизаций и переносу тормозного генератора на вебворкер, ибо он неизбежно влиял на FPS.
вторая демка
Только представьте что получающийся при генерации граф дорог частями надо прогонять через сериализацию, чтобы передать от вебворкера на страницу. Реальный кошмар, очень легко забыть какие-нибудь важные параметры. При этом надо следить и обрубать уже пройденные экраны, чтобы не мусорить память.
От одного из промежуточных результатов всех в прямом смысле тошнило: на свой страх...
Из сложностей можно особо отметить случай, когда после уже сгенерированного экрана не удавалось найти для него продолжения, потому что какая-то рельса упиралась в соседнюю станцию, или в край экрана. Если сгенерировать новый экран за 30 проходов не удаётся, то происходит откат на 1 экран назад. Иногда это заметно графически, прямо на ходу поезда меняется будующий путь.
Ну и процесс слияния и раздвоения путей тоже добавил мороки. Мы нередко начинали ходить по кругу:
Переключение стрелок в итоге зарезали, и это нас всех очень расстроило.
Равно как и товарища, похожего на героя из GTA. Наверху решили что он слишком «быдловат»:
Дальше пошли препятствия (в прямом смысле, их надо было делать согласно ТЗ). Вот тут мы отомстили по полной программе и показали то как мы можем чинить препятствия: наш «арт-директор» ушёл в августовский отпуск и из-за длительной переделки генератора так и не успел согласовать многие моменты с заказчиком. Я регулярно подходил к компу, перечитывал пункт про препятствия, до меня не доходило что с этим делать, и в итоге тратил время на оптимизацию игры (разные 2d/webgl варианты, запихивание спрайтов в атлас), вместо того чтобы разобраться с художниками и уточнить сценарий. Недовольство по поводу отсутствия существенных апдейтов переходило по цепочке, начиная с главного заказчика, и останавливалось на kanobu.ru, которые терпели, но вели себя с нами совершенно корректно. За неделю до сдачи этот тупик совместными усилиями разрешился, был составлен подробный сценарий, нарисованы идиоты и другие элементы препятствий. В намеченный договором день всё работало как надо.
Серверная часть довольно простая, она умеет записывать результаты приходящие по ajax-запросам. Проблем с ней не было, всё было сделано в последний день, и даже хватило времени с защитой от самых очевидных хаков. Думаю, те кто сломают систему так что я это не замечу, достойны призов.
Уже после запуска проекта выяснилось что на многих устройствах генератор слишком тормозит и не успевает построить путь спереди. Выход оказался тривиальным — поменяли json-формат карты на бинарный и сгенерировали на серверной стороне 100 тысяч экранов. Это стало возможно поскольку игра разделена на node.js модули и собирается с помощью browserify. Игрок начинает на случайном экране в первой половине, и до того момента, как кешированные экраны кончатся и придётся вместо подгрузки экранов с сервера задействовать генератор пройдет очень много времени :)
Этот подход дал неожиданный эффект: на «дальнем востоке» появились очень кривые дороги.
Из-за того, что в бинарке мы писали float а не double, на экранах с огромной x-координатой (десятки миллионов) стало не хватать точности координат, рельсы стыковались с ошибками в 1-2 пикселя. Да, Дальние Земли начались у нас гораздо раньше чем в майнкрафте. Та же ошибка стала проявляться при прорисовке рельс
Кривизну дальневосточных рельс исправил переход от глобальных координат к локальным относительно экрана.
Чтобы начинать с любого места, вся дорога делится по десяткам экранов, и хранится в разных файлах. При этом конечные сегменты рельсы надо дублировать и в предыдущем и в следующем файле, иначе не получится начать езду не с первого файла. И вот эту дублированную рельсу мы забывали удалять при загрузке очередного десятки экранов, а поскольку координаты рельс расчитывались несколько извратно чтобы не заехать в дальний восток, результат выглядел просто шикарно:
Реки и мосты — одна из самых извратных частей генератора. Дело в том, что река протекает сразу через несколько экранов и сильно влияет этим на возможность продолжения пути нашим случайным генератором. Достаточно часто экран с началом реки попадает под откат, реки просто не выдерживают эволюционного отбора. Тщательный подбор констант и следюущий трюк решили эту проблему: когда какая-то рельса пересекает речку, генератор старается её удлиннить и поставить под неё мост. В процессе перебора пути, если рельсу удаляют, то мост надо удалить вместе с ней. Правда, в некоторых случаях удалялся только мост. Так появился распилочный баг.
Перспективы у игры отличные: на основе получившегося генератора можно сделать много новых проектов. Можно добавить приколов и реализма, которого все так жаждут (если поезд не задавит идиота, то хоть охрана выйдет и отомстит).
Можно расписать о том что мы сделали инновационный искусственный интеллект, который генерирует пути для РЖД, выиграть какой-нибудь приз в стартаперских тусовках, найти инвестора…
P.S. Специально для хаброжилов с сегодняшнего дня где-то около 2015-ой отметки стоит небольшая пасхалка.
P.P.S Статья случайно была опубликована изначально на ГТ, давно не постились ;)
Комментарии (21)
serafims
18.09.2015 16:47+2Вообще, странно, вроде все в детстве хотели железную дорогу, а реально игр теперь на ЖД тематику (ну типа СимСити, только с упором на ЖД) совсем немного выпущено…
Jedi_Knight
18.09.2015 16:59+12yarg
19.09.2015 10:36+2Видимо, тема поездов актуальна всегда. Вот буквально 3 дня назад мы зарелизили ремейк шортлайна — train-valley.com
Extremum
18.09.2015 17:48+1А я сразу заностальгировал по Tycoon.
Lux_In_Tenebris
18.09.2015 18:37+7Так Transport Tycoon живее всех живых в виде OpenTTD. Есть ещё ремейк Locomotion, в этом году ставший доступным в Steam.
JustRoo
18.09.2015 18:35+1Их как раз немало для такой узкой ниши, имхо. Есть OpenTTD для ностальгирующих, Sid Meier's Railroads для тех, кому нужны няшные паровозики, Train Simulator для тех, кто в детстве мечтал быть кочегаром, и Train Fever для тех, кому важна экономическая составляющая. Куда больше?
Trotil
18.09.2015 17:55+2Хм, дизайн поезда взяли с электропоезда Ласточка, а электрификации в демках не видать. Дизельные Desiro выглядят немного по-другому.
micb
18.09.2015 18:26+1А это РЖД попросили сделать человечков, которые бегом догоняют движущийся поезд, так сказать, для большей реалистичности?
DaveDee
18.09.2015 18:31Именно:) В ТЗ было что-то вроде «поджидают в кустах и забираются на ходу»
Изначально мы планировали их пускать когда поезд притормаживает или останавливается на станции…
TimsTims
19.09.2015 14:45Ждем образовательную статью, как написать бота для этой игры :)
Jedi_Knight
19.09.2015 21:34Надо либо вклиниться в логику и следить за появлением препятствий, либо вклиниться в рендеринг (pixi) и следить за появлением маркеров препятствий, отсылать им мышиный клик.
Гораздо более интересно подменить 2д на 3д :)
TheRabbitFlash
22.09.2015 05:14+2Я хоть и любитель всего флешового, но эта статья, пожалуй, лучшая на хабре про html5/webgl, где хочется похвалить автора.
Спасибо за адекватную и нормальную статью и игру, а не поделки из серии «Флеш говно потому, что вот hello world на html5». От всей души плюсую!Jedi_Knight
22.09.2015 11:12Большое спасибо!
Хотел написать ответ, но почему-то вышел список ссылок на другие проекты :)
Да, флеш никак не говно, как минимум потому что на нём есть танчики, рисуют ниндзю, и autodesk scaleform.
На флеше я помню замечательные вещи от Nicklaus Liow (The Game, Reimagine: The Game), правда он потом на HTML5 перешёл (nothing to hide), но он вообще много чего меняет, он известный транс.
Кстати, чисто по webgl впереди всех сейчас лондонские товарищи из ga.me и playcanvas.com, но это возможно до тех пор пока artillery.com не выйдут из закрытой альфы. Среди 2d-движков конечно pixi.js (YAY!) и libgdx (java, люблю её).
Поскольку часть ресурсов у нас рендерилась в блендере, то я попробую это всё засунуть в playcanvas в качестве демки :)
ironwool
22.09.2015 13:08Равно как и товарища, похожего на героя из GTA. Наверху решили что он слишком «быдловат»:
Это зависит от контекста,
мне вот тоже так показалось (что лишком «быдловат»).
А какова его роль в игре?DaveDee
22.09.2015 18:58Просто объяснять игроку, что лихачить на жд путях — не самое умное занятие.
Текст финальный для него так и не был утвержден, но никакого налета ганстерщины не предполагалось.
romy4
Так стрелки всё-таки есть где-то или убрали?
П.С. Поворот поезда определённо нравится. Чего нехватает во всех Tycoon-ах.
Jedi_Knight
Убрали возможность переключать стрелки, путь по которому едет поезд берётся от балды. Во второй демке эта возможность есть.
Поворот поезда сделан в стиле древних стратегий, CnC, red alert, правда там не было трюка с доворотом спрайта до нужного угла.