BattleGIS — JavaScript-движок для игры в Танчики
Танчики на денди — классический пример синдрома утёнка в плоскости видеоигр. Современные игры настолько круты, что сложно отличить скриншот от фотографии. Однако взрослые дядьки могут часами двигать по экрану восьмибитные пиксели, вспоминая счастливое детство.
Хотелось сделать чего-нибудь эдакого, чтоб можно было собраться с коллегами в большой переговорке и за разного рода напитками весело провести время. Это должно было быть связано с профессиональной деятельностью: JavaScript`ом или вёрсткой, ведь чтобы погамать в Кваку или выехать на шашлыки, быть frontend-разработчиком совсем не обязательно.
Так родилась идея сделать JavaScript AI-challenge.Увы, но большинство подобных платформ нам не подошли: обязательным условием была реальная, наблюдаемая состязательность и низкий порог вхождения. То есть искусственный интеллект должен писаться от 5 минут до 3 часов JavaScript-программистом любого уровня, а не несколько недель группой сишников, как, например, в AI Cup`ах от Mail или Google.
Хотелось сражения, написания кода и соревновательности.
ПроектИсходя из вышесказанного, реализация должна была удовлетворять некоторым требованиям.Максимизация фана от игры, и от использования новых технологий по ходу реализации проекта. Нижайший порог вхождения написания ИИ. Realtime-битва на большом экране. Обновление «мозгов» на лету. Кроссбраузерная поддержка последней версии хрома. С точки зрения фана альтернатив танчикам не было: это зрелищная realtime-игра с простой и знакомой всем механикой. Выбор танчиков отчасти удовлетворял и второе требование: в денди у вас есть всего пять кнопок: крестик и стрелять. Ровно столько базовых методов должно было быть в прототипе ИИ: это радикально снизило порог вхождения, и первое, что сделали большинство участников, написали рандомного бота: this.move (['left', 'up', 'right', 'down'][Math.floor (Math.random () * 4)]); this.fire (); Ездить можно только по прямой, стрелять только куда едешь — это определяющие правила игры. Всё остальное (ускорения, заносы, отдача от стрельбы, накопление боезапаса) вводилось постепенно и с главным условием: влияние на геймплей и результат от каждой новой фичи должно быть минимальным.Реализация Для обсчета всей математики (положения, коллизии, попадания), на Node.js был написан движок. Довольно быстро возникла проблема дебага, решением которой стала клиентская Browserify-сборка, которая отлично отлаживается в DevTools Хрома.На раннем этапе «тонкий» клиент, сделанный для визуализации боев, ajax«ом запрашивал последние N кадров битвы и отрисовывал их dom-элементами (без шуток!). Было очень смешно, когда dom-снаряд, ударившись о цель, по диагонали улетал к другому танку и выстреливался вновь… в общем, вместе с шумом вентиляторов потеющих ноутбуков, к нам быстро пришло понимание, что без Canvas мы не обойдёмся (спасибо библиотеке PIXI).
Для сетевого взаимодействия мы использовали Socket.io. Когда используешь вебсокеты, понимаешь, что 21 век наступил. Триггер события на одной машине, слушание этого события на другой, потоки… на сервере мы просто пишем в поток кадры, а на клиенте эти кадры слушаем. Никакой буферизации, проблем с памятью и рассинхроном.
Кстати говоря, нам хотелось написать что-нибудь на фреймворке Meteor. «Чем-нибудь» стала наша клиентская часть. Модульность, высокоуровневые API, пакеты, изоморфность, Liver Reload — хоть и избыточные в нашем случае, но очень приятные штуки.
Самым интересным требованием, с точки зрения технологий, была замена игроков «на лету»: битва идёт, танк стреляет и ездит, а его «мозги» в любой момент могут полностью поменяться по желанию автора. Здесь не может быть премодерации: код в течение секунды после отправки должен начать исполняться на сервере.
Сначала мы доверяли друг другу, поэтому new Function () вполне хватало. Но один хороший человек написал в своём боте process.kill (), и стало ясно, в OpenSource такой мультиплеер отдавать нельзя. Эффективно отлавливать регулярками всякие ужасы типа setTimeout (while (true)) не представляется возможным, и здесь бы нам на помощь могла придти абсолютно безопасная песочница, но мы хотели потрогать новомодную виртуальную машину.
С виртуальной машиной Node.js всё оказалось просто, но интересно: например, в текущей реально стабильной ноде версии 0.10.36, в контекст-объекте, переданном в виртуальную машину, отваливается прототип (а ещё там внезапно нет MAX_SAFE_INTEGER). В 0.12.0 тоже не всё хорошо, плюс наши рабочие проекты, типа 2gis.ru, на ней пока не работают, из-за проблем со сборкой некоторых npm-пакетов. В этой связи мы теперь используем замечательный npm-пакет «n» для быстрого переключения версии ноды.
Сама работа с виртуальной машиной проста: мы запускаем подготовленный eval-скрипт в контексте выбранного нами js-объекта. И у этого объекта нет setTimeout, console.log и прочих штук, которые есть в global-объекте. Кроме того, виртуальная машина позволяет ограничивать выполнение кода по времени: если код работает долго, он убивается, а его последний вздох ловит ближайший в стеке catch.
В итоге Мы изучили вебсокеты и виртуальную машину Node.js, потыкали палочкой Meteor и PIXI, пописали ИИ, для многих первый в жизни, ну, а главное — сразились друг с другом и получили много фана.Побочным эффектом Browserify-сборки стал синглплеер, который вы можете пройти уже сейчас: battle.2gis.ru. Посмотреть на сам движок можно здесь: github.com/2gis/Battlegis.
В ближайшем будущем мы планируем сделать выделенный сервер с полноценным мультиплеером, где каждый желающий сможет потягаться силами с другими JavaScript-программистами; ну и, конечно, заняться багофиксом и кроссбраузерностью. Следите за новостями!