Во время обдумывания чем можно было бы завершить цикл “Новый взгляд на старые игры” в памяти всплыл специфический, технический текст, перевод которого некогда застрял на стадии полировки терминологии. Сегодня вашему вниманию предлагается, насколько то дозволила сложившаяся специфика, доработанная версия.
Об оригинальной игре я ранее рассказывал в рамках материала из другого цикла, здесь же речь пойдёт именно о ремейке и, преимущественно, его front-end специфике.
Перевод выкладывается с разрешения Scott-а Schiller-а. Характер статьи изобилует заметками / элементами монолога автора с самим собой. При переводе было решено оставить заданную подачу как есть, без радикальной стилистической коррекции или смены формата.
Осуществлял дополнительный анализ JavaScript-терминологии – oldalexi. Выступал в качестве дополнительного редактора – Newbillius.
Эволюция разных версий ремейка от самого раннего прототипа до текущих дней.
Я играл в Armor Alley в начале 1990-х на старом IBM-PC на базе 8088 процессора; здесь вы найдёте мою интерпретацию данной игры.
Прямо сейчас её можно запустить здесь – http://www.schillmania.com/armor-alley/.
Браузерная интерпретация классической игры 1990 года
Armor Alley — аркадный сайд-скроллер, который также можно назвать симулятором со стратегическими элементами. Основная цель — доставить фургон через поле боя к вражеской базе, но конечно, сделать это будет не так просто; фургон совершенно безоружен и крайне уязвим. Вы должны защищать фургон силами колонны, состоящей из танков, ракетных установок и других наземных единиц, а также обеспечивать воздушное прикрытие с помощью вертолёта. Силы противника идентичны вашим.

Краткая демонстрация веб-прототипа АА в обучающем режиме. Показана основная механика, объяснены элементы обороны и наступательной тактики.
Зачем всё это?
– … Затем.
Любое приложение, которое может быть написано на JavaScript, в конечном итоге будет написано на JavaScript. — Закон Этвуда (2007).
Написание браузерной игры с использованием HTML, CSS и JavaScript – отличный способ учиться, переучиваться и экспериментировать с подходами к программированию на клиенте. Игры требуют глубины логической мысли, планирования, усилий для построения, а также поощряют эксперименты по части архитектуры и самого стиля разработки.
Производительность и масштабируемость являются важными факторами при создании игр и оба предоставляют возможность для обучения написанию производительного кода, совместимого с чередой платформ и устройств. Независимо от точности соответствия оригиналу, работающий прототип игры может быть интересен как с образовательной стороны, так и с развлекательной.
Введение
Создавайте и защищайте колонны, состоящие из танков, ракетных установок, пехоты, фургонов и инженеров, по мере того, как они пересекают поле боя, используйте свой вертолет как для нападения, так и для защиты. Конечная цель состоит в том, чтобы доставить свой фургон до вражеской базы на другой стороне поля боя.
Изучение оригинала
PC, версия для MS-DOS, год выхода – 1990, порт оригинальной версии для компьютеров Macintosh.
Сайд-скроллер, высота фиксированная, 16 цветов, паттерны, которые можно переиспользовать (автомобили, шрапнель, стрельба, дым, взрывы), минимальная анимация, многочисленное взаимодействие между транспортными средствами и смена их поведения в окружающей среде. Варьирующаяся, но в целом неглубокая и ограниченная сложность; относительно прямолинейная реализация.
Принятые ограничения проекта
Полновесная реализация на клиентской стороне: HTML, JavaScript, CSS. Первый “уровень”, стандартная техника и элементы местности, включая зенитные турели / “пушки”, но без бронированных бункеров. Базовый “ИИ” противника, автоматизированное построение конвоя / отдача приказов + контроль действий вражеского вертолета / защита — вероятно будет трудно по настоящему подражать исходному поведению. Сетевой режим и мультиплеер отсутствуют.
Процесс разработки

Первоначальный прототип: базовый ландшафт, местность, транспортные средства, бункеры с воздушными шарами и передвижение транспорта. Вертолет игрока может летать над местностью.
Добавлена вражеская техника. Добавлены основы обнаружения врагов, стрельбы, бомб и обнаружения столкновений.
Пехота, бункеры, взаимодействие с транспортными средствами.
Строка состояния, посадочные площадки топливопровода + действия по ремонту / заправке / перезагрузке. Умные ракеты и радарная система. Система инвентаризации / заказа. Поиск / обнаружение близлежащих объектов (умные ракеты, ИИ).
Устранение неполадок и отладка
Chrome DevTools: фреймы, память, профилирование JS / CPU.
Преобразования CSS + JS feature detection.
Горячие циклы, создание объектов / использование памяти / сборка мусора.
“Архитектура”
Ванильный JS, SoundManager 2 для аудио. Старые добрые элементы DOM для рендеринга пользовательского интерфейса против <canvas> или WebGL и т.п. Преимущества: естественный DOM createElement() для создания игровых объектов, CSS для их стилизации, манипуляции на основе className, трансформации и анимации.
JavaScript: utils (утилиты) для манипулирования именами классов CSS, событиями DOM, удаления узлов дерева, примесей объектов, клонирования и т.п.
Контроллеры, то есть gameLoop, перебирают коллекции игровых объектов, вызывая метод animate() каждого объекта. Если animate() возвращает true, объект мёртв и контроллер может удалить его из массива. Шаблон повторяется для коллекций транспортных средств, стрельбы, зданий и т.п.
MVC-подобная тема: css, data, dom, интерфейс объектов определён для основных игровых объектов. У некоторых объектов есть дочерние и / или родительские объекты, например, бункер <- цепь -> воздушный шар.
frameCount и модуль определяют интервал поведения — движение, стрельбы, скорости анимации, обнаружения врага и т.п. — то есть if (frameCount % 10 === 0) { fire(); }
Имена объектов + карта типов сопоставляются между именами массивов, паттернами конструктора, именами классов CSS (в общем случае). Например у фургона есть тип (data.type = ‘van’), CSS-свойство .van, он хранится в game-objects.vans и так далее. Каждый объект имеет предсказуемый шаблон DOM, имя класса CSS и структуру данных.
isEnemy применяется к JS, содержит класс .enemy в CSS. Пользовательский интерфейс + логика столкновения, в остальном, в основном, одинаковы.
Обнаружение столкновений / взаимодействие вида враг + объект
nearbyOptions – “в кого стреляют?”
nearbyObject() – “is an X (то есть, вертолёт) в зоне досягаемости?”
Object targeting – “двигаться в сторону вертолёта”
Если есть перекрытие объектов, вызвать target.hit() и предоставить “исходный” интерфейс объекта. Цель определяет взаимодействие — то есть цель может умереть, но может и убить источник.
Анимации
Комбинация style.left / top, некоторых анимаций спрайтов на основе backgroundPosition, а также анимаций и трансформаций CSS. Пошаговая анимация CSS обеспечивает удобные переходы, запускаемые className, например, взрыв танка: .tank.dying {} -> .tank.dead {} Метод animate() применяет vX + vY к x + y, обновляет свойства style.top / left (традиционный подход) или transform (ускорение GPU) для изменения положения узла DOM.
“Наследование”
Наследование данных, структур CSS и т.д. на основе миксинов. Общие имена классов CSS (состояния), атрибуты данных, такие как x, y, dead, isEnemy и т.п. Общие операции: перемещение спрайта (DOM x / y), объект находится слева в объекте верхнего уровня common, аналогичном utils.
Производительность
Используем transform: translate3d(), если поддерживается движение элементов на основе графического процессора по осям x / y, по сравнению с традиционными изменениями стиля слева / сверху. Translate позволяет избежать дорогостоящих перерисовок, вместо этого используя композитинг на основе графического процессора для движения.
JS: избегание создания чрезмерного мусора (например, клонирования объектов в стиле миксина) в горячих / дорогих циклах; уменьшение использование GC, оперативной памяти и общую текучесть кадров. Передаём объекты напрямую / по ссылке, избегая создания новых объектов или изменения исходных значений объектов в циклах.
Разрушение / очистка объекта: удаляем дерево узлов, ссылки JS / DOM и ссылку на родительский массив в случае коллекции объектов. Минимизируем “ввод-вывод” DOM: кэшируем ссылки и координаты узлов, чтобы уменьшить перекомпоновку из-за операций чтения (например offsetWidth). Обновляем клиентские координаты только при критических событиях, таких как init и window.onresize().
Память, количество узлов DOM и сборка мусора JS / DOM
Нормальный игровой процесс и распределение памяти показаны выше синим цветом, наряду с растущим числом ссылок на узлы DOM (зелёным). В этом случае пулемёт вертолета стреляет 64 раза и узлы добавляются линейным образом. Когда объекты стрельбы уничтожаются всё успокаивается и в конечном итоге происходит событие сборки мусора.
Когда узлы JS / DOM разыменовываются посредством уничтожения / очистки объекта JS, оставшиеся узлы DOM могут быть надлежащим образом удалены сборщиком мусора. Естественное событие GC отражает это в середине, за которым следуют остальные новые узлы с ручным событием GC, вызванным ближе к концу.
“Detached Dom Trees” Chrome DevTools в разделе “Heap Snapshot Profile” также может пригодиться для поиска утекших узлов DOM; отсоединенный DOM включен в представление сдерживания. На момент написания (1 ноября 2013 г.) могла существовать ошибка, связанная с тем, что узлы DOM с ускорением на GPU не подвергались сборке мусора или просто не отражались на графиках Chrome DevTools. В контексте подробностей можно ознакомиться с информацией здесь – https://code.google.com/p/chromium/issues/detail?id=304689.
“Искусственный интеллект”
На самом деле достаточно глуп. “Логика, основанная на правилах” — более подходящее описание этой реализации.
Умные ракеты: двигаются прямо к цели, плюс небольшое отклонение с изменением ускорения.
Вражеский вертолет: нацеливается на ближайшее облако, воздушный шар, танк или вертолет игрока, если он находится в пределах досягаемости. Фиксированная скорость ускорения, нормализуется до 0, когда находится “достаточно близко” к цели. Возвращается на базу, когда закончились боеприпасы + бомбы, топливо или сильно повреждён. Не уклоняется от целей и препятствий. Враг может прятаться в облаках, бомбить проходящие танки в пределах досягаемости, если подобное применимо.
“Режим воздушного боя”: стремится равняться с вертолётом игрока. Стреляет из пулемёта, когда находится в пределах досягаемости. Если игрок находится прямо под ним, то пытается взорвать его. Препятствует прямому пролёту над или под собой.
Флажки “прицеливания”: облака, танки, вертолёт, воздушные шары. Если несколько целевых вариантов, логика определяет приоритет. (грубое предпочтение, от низкого к более высокому: облака, воздушные шары, вертолеты, танки).
Построение / сортировка конвоя противником: последовательность “MTVIE” через фиксированные промежутки времени – один раз в несколько минут, в зависимости от наличия средств. Вражеский вертолет имеет небольшое преимущество в скорости, что затрудняет погоню или бегство от него.
transform:translate начальное положение
При использовании преобразования графического процессора, перевод в Chrome: обнаружены нечётные / случайные проблемы с перерисовкой, если style.left / top или transformOrigin изначально не назначены. Логический; в противном случае браузер скажет: “преобразовать этот элемент относительно чего?”… Рекомендация: применить начальные значения top / left 0px и / или transform-origin в CSS.
Звуковые эффекты
Звук значительно улучшает впечатления от игры.
Оригинальные 8-битные звуки нельзя было повторно лицензировать; современные замены (и новые звуки) были смешаны из многочисленных Creative Commons и бесплатных источников по типу freesound.org. В частности благодаря звукам Hi-Fi стало веселее взрывать те или иные объекты.
Расстояние влияет на громкость и, в идеале, на эффекты панорамирования звуков (звуки за кадром более тихие и т.п.).
Разные примечания по эффективности и производительности
Обнаружение столкновений — это просто математика. Кэширование / аннулирование, вероятно, будет более дорогим и не стоит затраченных усилий. То же самое касается других простых проверок координат, например, объекта поблизости / на экране / прицеливания. Большая часть времени тратится на GPU / железо, выполнение операций отрисовки / разметки /рендеринга.
Вещи, которые сработали
Согласованное соглашение об именах внутри объектов, открытый интерфейс через exports = { css, data, dom }; return exports;
Общие методы: animate(), hit(), die() и т.п.
Массивы объектов (фургоны, танки, бункеры) + один контроллер верхнего уровня, цикл, который вызывает animate() для каждого элемента и соответственно удаляет “мертвые” элементы.
Столкновение содержит интерфейсы exports, стандартные свойства, как data и функция hit().
hit() принимает необязательное значение точки и исходного / целевого объекта. В некоторых случаях оба объекта могут быть повреждены или уничтожены. JS меняет имена классов CSS в зависимости от состояния: .enemy, .dying, .dead и так далее. Общие методы “создания” объекта, необязательный параметр / опция, то есть конфигурации, например, isEnemy, x, y, vX, vY.
Интервалы на основе frameCount, устанавливающие анимацию + скорость поведения, например, move() для каждого кадра, fire() только два раза в секунду, обнаружение врагов один раз в секунду и т.п.
Конфигурация поблизости и “столкновение” – легко определить “по кому стреляют”, например, танки -> пехота. “Упреждающий просмотр” по умолчанию влияет на способность транспортного средства “видеть” перед собой.
“utils” для базовых событий DOM, манипулирования именами классов CSS, удаления дерева узлов.
Пакетные изменения DOM, в частности постановка в очередь и удаление узлов в результате деструкции объектов (т.е. гибели множества экземпляров объектов типа GunFire).
Оглядываясь назад: вещи, которые я бы изменил или пересмотрел
Пересмотр наследование объектов, данных и функций — могут ли почти все игровые объекты наследоваться от каких-либо “спрайтов”, основанных на сортировке.
Могут быть исследованы и реализованы более интеллектуальные алгоритмы обнаружения столкновений.
Трансляция событий? Будет ли это разумно использовать с точки зрения абстракции? (До сих пор – вопрос).
Различные “экспорты” / API для каждого объекта? Больше абстракции, меньше предположений о css, data, dom? Улучшенная абстракция “спрайта” для каждого объекта. Проще манипулировать DOM?
Спрайты в CSS ранее на / SASS / compass для автоматической оптимизации.
Избегание написания каких-либо вызовов setInterval() / setTimeout(). В настоящее время используется для задержек после взрыва перед уничтожением объекта (удаление узла DOM, очистка объекта). Более умное решение: использовать существующий цикл анимации, чтобы применить действие после заданного количества кадров и таким образом уничтожить объект. Частично реализовано.
TODO: настройки и другие мелочи, чтобы привести всё в порядок
Удалить вызовы setTimeout(), используемые для уничтожения объектов, вместо этого перейти к использованию animate() + frameCount для синхронизации (некоторые из них реализованы — см. FrameTimeout() в коде для справки).
Пересмотреть создание объектов, выделение памяти и сборку мусора. В настоящее время не так уж плохо, но всегда есть возможности для улучшения. Объединение объектов можно использовать для обычных объектов, таких как стрельба и т.п.
Дальнейшие соображения по оптимизации: спрайты изображений и звуковые спрайты, где применимо. Удалить анимированные .GIF, сместить акцент на анимацию спрайтов + CSS, если это будет быстрее / плавнее.
Особенности, которых нет в оригинальной игре
Режим обучения: вводное руководство по игровой механике, задачам и основным стратегиям.
Возможность спрятаться в облаках / замаскироваться от радара (возможно было реализовано в оригинальном многопользовательском режиме, здесь не уверен).
Вражеский вертолёт может прятаться в облаках и может бомбить проходящие танки (“режим скрытой облачной бомбардировки”). С другой стороны, игрок может незаметно проходить над вражескими зенитными орудиями, ракетными установками и вражеской базой, а также над вражеским вертолетом. Добавляет в игру элементы веселья и стелса.
Дополнительные звуковые эффекты для вертолёта, пехоты с парашютом, стрельбы со стороны последней, заклинивания фургона и попадания осколков по объектам.
Вещи, до которых я никогда не доберусь
Мультиплеер. Не из-за отсутствия интереса; думаю это было бы здорово, только времени нет.
Эпилог
Ссылки по теме:
Сыграть в веб-прототип – http://www.schillmania.com/armor-alley/
Фотоальбом (заметки, скриншоты процесса разработки, отладка и т.п.) – http://www.flickr.com/photos/schill/sets/72157634655626918/
Armor Alley (веб-прототип) на GitHub – https://github.com/scottschiller/ArmorAlley
Титры + благодарности (веб-прототип) – http://www.schillmania.com/armor-alley/CREDITS.txt
Armor Alley в Wikipedia – http://en.wikipedia.org/wiki/Armor_alley
Автор: UnknownDoomer


