За последние пару лет количество веб-игр HTML5 в сети резко возросло, во многом благодаря игровым движкам HTML5, которые значительно облегчают их разработку.
В этом уроке я покажу вам, как построить простую космическую игру с астероидами, используя игровой движок Impact , один из самых мощных на данный момент.
Impact — это коммерческий движок, поэтому вам необходимо приобрести лицензию, прежде чем вы сможете его использовать, но это не мешает вам, по крайней мере, следовать учебному пособию, чтобы понять, как оно работает.
Вам также понадобятся эти игровые ресурсы в учебном пособии, поэтому я предлагаю вам загрузить их сейчас, если вы планируете следовать.
Иногда тратить деньги это хорошо
Давайте уберем это с пути; Воздействие не бесплатно. Если вы похожи на меня, вы, вероятно, немного разочарованы этим утверждением. В конце концов, мы используем открытые веб-технологии, так что зачем брать плату за то, что основано на бесплатной технологии. Правильно? Ну, проще говоря; Разработчик должен зарабатывать на жизнь. Кроме того, и, вероятно, самое главное; двигатель Impact — чрезвычайно надежный и хорошо документированный двигатель. Я бы даже сказал, что это лучший игровой движок HTML5, с которым мне доводилось играть на сегодняшний день, хотя он и не на 100% совершенен (что это?).
Я полностью уверен, что цена на двигатель является отражением времени и усилий, затраченных на его создание и поддержку. Короче говоря, если вы не можете позволить себе $ 99 за лицензию, тогда я предлагаю вам взглянуть на некоторые из бесплатных игровых движков HTML5 .
Обзор игрового движка Impact
Как я уже упоминал ранее, Impact — это надежный игровой движок, который явно потратил много времени и усилий на его разработку. Он работает на всех основных браузерах, поддерживающих HTML5 canvas, а также на iPhone, iPod Touch и iPad. На мой взгляд, Impact на голову выше конкурентов — это тщательная документация , учебные пособия , а также полезная поддержка, доступ к которой можно получить через форумы . В Impact определенно ощущается общее качество, которого нет у других двигателей.
В Impact встроен целый набор функций, таких как объекты (объекты в игре), физика, анимация, пользовательский ввод, обнаружение столкновений, аудио и даже полноценный редактор уровней (подробнее об этом чуть позже). Это ни в коем случае не является окончательным списком того, что может сделать двигатель, но я надеюсь, что он хотя бы пролил некоторый свет на то, сколько утомительной работы он может снять с рук. Этому способствует способ создания движка с использованием объектно-ориентированного подхода, который позволяет эффективно подключать и играть с новыми функциями в вашей игре, расширяя возможности уже встроенного. Это быстро, легко и просто так, как вы хотите. занятый разработчик игр.
Редактор уровней Weltmeister — это одна из главных жемчужин Impact, и, пожалуй, одна из самых визуально впечатляющих особенностей движка. Этот редактор уровней по сути является графическим интерфейсом, который позволяет вам создавать свой игровой мир, от простого проектирования уровней до размещения объектов и определения их взаимодействия друг с другом. Это довольно впечатляюще!
Важно отметить, что вы не будете использовать редактор уровней Weltmeister в этом учебном пособии, так как я хочу научить вас необработанному коду, но я бы порекомендовал посмотреть это видеоурок о редакторе, чтобы понять, на что он может делать.
Что вы будете делать
В этом уроке вы создадите простую космическую игру, в которой вам нужно будет избегать астероидов. Пока вы будете пропускать такие функции, как звук и съемка, поскольку они пока что слишком усложняют ситуацию, но будьте уверены, что это функции, которые вы определенно сможете добавить позже в свое время без особых хлопот.
Почему бы вам не проверить это видео игры в действии?
Настройка Impact
Прежде чем вы запачкаете руки игрой, вам нужно настроить движок Impact. Это относительно безболезненно, но я собираюсь осветить это подробно, потому что важно знать, что происходит. Я бы определенно проверил страницу документации о настройке Impact для более детального прохождения.
Использование домена для разработки
При разработке с Impact необходимо помнить о том, что вам нужно работать на веб-сервере и получать доступ к игре с помощью доменного имени. Вы можете использовать онлайн-сервер или локальный сервер разработки через MAMP (Mac), WAMP (Windows) или XAMPP (кроссплатформенный). Это требование связано с проблемами безопасности в JavaScript и никак не связано с механизмом Impact.
Скачивание и распаковка
После покупки Impact вам будет отправлено электронное письмо с персональной ссылкой для скачивания и лицензионным ключом. Я надеюсь, что вы держите это в безопасности! Эта ссылка для скачивания привязана к вашей учетной записи, поэтому, если вы потеряете ее, вам придется связаться с разработчиком, чтобы получить ее повторно. Первые шаги — загрузить личную копию движка, распаковать zip-файл, открыть папку последствий и переместить файлы и папки внутри на ваш веб-сервер. Вы узнаете, все ли прошло хорошо, потому что вы увидите сообщение на экране, когда загрузите файл index.html в свой браузер.
Файлы, которые вы только что скопировали, являются файлами основного движка ( папка lib / Impact), а также некоторыми примерами файлов, которые вы будете использовать для создания своей игры ( папка lib / game ). Другие файлы, которые вы видите, связаны с редактором уровней Weltmeister ( папка lib / weltmeister ), а также с некоторыми инструментами, которые помогут при упаковке игры для публичного выпуска ( папка tools ).
Во время этого урока вам нужно беспокоиться только о папке lib / game , а также о файле index.html .
Настройка HTML и CSS
HTML-файл по умолчанию использует встроенный CSS, поэтому давайте выполним некоторые настройки и перенесем CSS в отдельный файл.
Откройте файл index.html , удалите все и добавьте следующий код:
<!DOCTYPE html> <html> <head> <title>Impact Asteroids Game</title> <meta charset="utf-8"> <link href="game.css" rel="stylesheet" type="text/css"> <script type="text/javascript" src="lib/impact/impact.js"></script> <script type="text/javascript" src="lib/game/main.js"></script> </head> <body> <canvas id="gameCanvas"></canvas> </body> </html>
Затем создайте новый файл с именем game.css и поместите его в том же направлении, что и index.html . Добавьте следующий код в файл CSS:
* { margin: 0; padding: 0; } html, body { height: 100%; width: 100%; } canvas { display: block; } body { background: #000; } #gameCanvas { height: 512px; left: 50%; margin: -256px 0 0 -384px; position: relative; top: 50%; width: 768px; }
Все это обеспечивает действительно простой сброс CSS, а также расположение игры в браузере по центру. Обратите внимание, как вы установили ширину и высоту игры в CSS на довольно определенный размер. Я объясню, почему вы выбрали эти измерения в данный момент.
Настройка основного файла игры
Если вы обновите браузер, вы увидите только черный фон. Это потому, что вы просто сломали игру, поиграв с HTML и изменив идентификатор элемента canvas. Упс! Давайте разберемся с этим.
Сначала откройте файл lib / game / main.js , который является основным файлом вашей игры. Я сейчас объясню, что делают различные разделы этого файла, но сейчас мы просто хотим внести пару изменений, чтобы вернуть это сообщение на экран.
Найдите вызов ig.main в конце файла и замените его следующим:
- ig.main( "#gameCanvas", MyGame, 60, 768, 512, 1 );
Все, что вы здесь сделали, — измените первый аргумент, чтобы он указывал на новый идентификатор элемента canvas, который вы изменили ранее. Другие изменения были внесены в четвертый и пятый аргументы, чтобы отразить новые измерения игры, а также в последний аргумент, чтобы остановить масштабирование игры в два раза. Вы используете определенные размеры, потому что движок основан на плитках, и плитки, которые вы собираетесь использовать, в основном имеют размер 64×64 пикселей — 768 и 512 просто делятся на 64, и все.
Ознакомьтесь с документацией по функции ig.main, если вы хотите узнать больше о том, как она работает.
Если вы обновите окно браузера, вы должны снова увидеть предыдущее сообщение, хотя на этот раз оно должно выглядеть немного меньше, потому что вы остановили масштабирование игры.
Модули
Стоит потратить несколько минут, чтобы объяснить, как работают модули, поскольку они будут тем, что вы используете для создания своей игры. Вот код модуля из файла main.js, который вы только что просмотрели, с удалением некоторых частей, которые пока не важны:
ig.module( "game.main" ) .requires( "impact.game", "impact.font" ) .defines(function(){ // Main module code });
Первый раздел, ig.module, определяет новый модуль, имя которого объявлено во второй строке — в данном случае «game.main». Имя представляет структуру папки, поэтому «game.main» относится к файлу с именем main.js, который существует в папке lib / game . Это довольно просто. Во втором разделе требуется список модулей, которые необходимо загрузить перед запуском этого модуля. Каждый модуль следует тем же правилам именования, что и раньше, поэтому «impact.game» относится к файлу game.js в папке lib / impact . Наконец, в разделе определения находится код модуля. Этот раздел не будет работать, пока не будут загружены все необходимые модули.
Создание игровых сущностей
Теперь, когда основной файл готов, пришло время создать объекты для вашей игры. Сущности в игре — это что-то интерактивное, например, игрок или враги
Астероидная сущность
Вы собираетесь настроить свои сущности как модули, поэтому первым делом создайте файл asteroid.js в папке lib / game / entity . Откройте файл и добавьте следующий код для настройки модуля лица:
ig.module( "game.entities.asteroid" ).requires( "impact.entity" ).defines(function() { // Subclassed from ig.Enitity EntityAsteroid = ig.Entity.extend({ // Set some of the properties size: {x: 64, y: 64}, // Entity type type: ig.Entity.TYPE.B, init: function(x, y, settings) { // Call the parent constructor this.parent(x, y, settings); }, // This method is called for every frame on each entity. update: function() { // Call the parent update() method to move the entity according to its physics this.parent(); } }); });
Это основной код большинства сущностей, который, как мы надеемся, вы должны узнать по большей части из файла main.js, который вы только что просмотрели, — это модуль.
Вы называете модуль лица вверху в ig.module, называет его «game.entities.asteroid», а затем объявляете, что необходимо загрузить модуль «effect.entity». Этот модуль предоставляет все основные функции для создания сущностей.
В разделе определения вы настраиваете свою сущность, заканчивая модуль сущности, например так:
- EntityAsteroid = ig.Entity.extend({
Первая часть — это имя сущности, а вторая просто говорит о том, что вы хотите расширить базовый класс сущности с Impact.
В вызове ig.Entity.extend вы размещаете весь код для определения астероидной сущности, начиная со свойств size и type. Свойство size относится к размерам объекта и не должно совпадать с размером изображения спрайта, которое используется для его отображения (вы заметите это при создании объекта player). Тип настройки позволяет группировать объекты в группу А или группу В. Например, это может быть использовано для отделения опасных объектов (B) от дружественных объектов (A).
Функция init вызывается при создании объекта, а update вызывается в каждом цикле игры, прежде чем что-либо будет нарисовано.
Прямо сейчас, астероидная сущность ничего не делает, поэтому давайте настроим ее и видим в игре.
Добавьте следующий код, где вы устанавливаете свойство типа:
// Load an animation sheet animSheet: new ig.AnimationSheet("media/asteroid.png", 64, 64),
Это устанавливает спрайт для астероида и позволяет вам анимировать его, если хотите. В вашем случае вы используете астероидный спрайт из игровых ресурсов, представленных в этом руководстве, но не стесняйтесь создавать свои, если хотите.
Далее следует определить анимацию для спрайта, что в данном случае является простым, поскольку вы вообще не хотите, чтобы спрайт анимировался. Добавьте следующий код под this.parent в функции init:
// Add animations for the animation sheet this.addAnim("idle", 1, [0]);
Первый аргумент называет анимацию и может быть любым, второй — время, чтобы каждый кадр анимации был виден в секундах, а третий — массив номеров кадров для анимации. В вашем случае есть только один кадр, который равен 0 (первый элемент массива всегда равен 0).
Вы пока ничего не увидите, поэтому перейдите в файл main.js и добавьте следующий код в верхнюю часть раздела require (не забудьте поставить запятую после предыдущего файла):
"game.entities.asteroid"
И добавьте следующее в функцию init:
var asteroidSettings; for (var i = 0; i < 8; i++) { asteroidSettings = {vel: {x: 100-Math.random()*200, y: 100-Math.random()*200}}; this.spawnEntity(EntityAsteroid, ig.system.width/2, ig.system.height/2, asteroidSettings); };
Это установит восемь астероидов в игре в середине экрана, каждый со случайной скоростью. По крайней мере, если бы вы добавили изображение астероида в игру. Для этого переместите asteroid.png из предоставленных игровых ресурсов и поместите его в папку media .
Если все прошло хорошо, вы должны увидеть коллекцию астероидов, расходящихся по центру экрана. Результат!
Пока вы находитесь в main.js , удалите весь код внутри функции draw, кроме this.parent. Это удалит сообщение с экрана, которое вам больше не нужно.
Граничные проверки
Проблема на данный момент в том, что астероиды исчезают за краем экрана и больше никогда не возвращаются. Это плохо. Вернитесь в файл сущности asteroids.js и добавьте следующий код под this.parent в функции обновления:
// Boundary checks if (this.pos.x > ig.system.width) { this.pos.x = -64; } else if(this.pos.x < -64) { this.pos.x = ig.system.width; }; if (this.pos.y > ig.system.height) { this.pos.y = -64; } else if (this.pos.y < -64) { this.pos.y = ig.system.height; };
Это проверяет положение астероида в конце каждого обновления и, если он находится за пределами игровой зоны, перемещает астероид в другую сторону игры. Это дает эффект бесконечного мира, где сущности не могут быть потеряны с края.
Сущность игрока
Теперь, когда базовая астероидная сущность создана, пришло время настроить сущность игрока. К счастью, большая часть основной логики одинакова.
Создайте новый файл player.js и поместите его в папку lib / game / entity . Добавьте следующий код:
ig.module( "game.entities.player" ).requires( "impact.entity" ).defines(function() { // Subclassed from ig.Enitity EntityPlayer = ig.Entity.extend({ // Set the dimensions and offset for collision size: {x: 28, y: 50}, offset: {x: 18, y: 7}, // Entity type type: ig.Entity.TYPE.A, // Load an animation sheet animSheet: new ig.AnimationSheet("media/player.png", 64, 64), init: function(x, y, settings) { // Call the parent constructor this.parent(x, y, settings); // Add animations for the animation sheet this.addAnim("idle", 0.05, [0]); this.addAnim("thrust", 0.05, [1,2]); }, // This method is called for every frame on each entity. update: function() { // Call the parent update() method to move the entity according to its physics this.parent(); // Boundary checks if (this.pos.x > ig.system.width) { this.pos.x = -64; } else if(this.pos.x < -64) { this.pos.x = ig.system.width; }; if (this.pos.y > ig.system.height) { this.pos.y = -64; } else if (this.pos.y < -64) { this.pos.y = ig.system.height; }; } }); });
Здесь нет ничего сумасшедшего нового. Одно из отличий состоит в том, что сущность имеет другое имя в разделе ig.module, а также в разделе определения. Другое заключается в том, что свойство size больше не равно 64×64 пикселям; теперь он составляет 28×50 пикселей, и на листе анимации ищется спрайт игрока (который вы можете найти в игровых активах в этом учебном пособии. Свойство меньшего размера представляет размер ракеты в изображении спрайта игрока, которое вы можете увидеть в этом образ:
Синяя область — это область, определяемая свойством размера, а ее позиция определяется свойством смещения. Это важно для уверенности, что при расчете столкновений используется правильная область спрайта.
Наконец, на этот раз настраиваются две анимации; один для того, когда игрок стоит на месте (холостой ход), и другой для того, когда игрок движется (толчок):
this.addAnim("idle", 1, [0]); this.addAnim("thrust", 0.05, [1,2]);
В анимации тяги есть два кадра, это означает, что спрайт будет циклически переходить от кадра 1 к 2, а затем снова к 1. Каждый кадр будет отображаться в течение 0,05 секунд. Вы увидите это через мгновение, но эффект будет в основном мерцающим пламенем.
Последний шаг — добавить объект игрока в основной файл игры, как вы делали это ранее с астероидами. Откройте файл main.js и добавьте следующую строку в верхнюю часть раздела require (не забудьте добавить запятую после предыдущего файла):
"game.entities.player"
Затем добавьте следующий код над функцией init:
- player: null,
И добавьте следующее, где вы добавляете астероиды в функцию init:
// Load player entitiy var playerSettings = {}; this.player = this.spawnEntity(EntityPlayer, 150, 150, playerSettings);
Пока ничего не появится, поэтому, как и в случае с астероидами, вам нужно переместить файл player.png из ресурсов игры, предоставленных в этом руководстве, в папку мультимедиа .
Обновите браузер, и вы увидите маленькую ракету в верхнем левом углу. Здорово!
Фон игры
Хотя это не настоящая сущность, это то, что нам все еще нужно настроить. Пока вы еще в main.js , добавьте следующее выше функции init:
- background: new ig.Image("media/background.png"),
Это устанавливает фоновое изображение, но оно еще не будет нарисовано. Чтобы нарисовать его, вам нужно заменить строку this.parent в функции draw на следующую:
// Draw the background this.background.draw(0, 0); // Draw all entities for(var i = 0; i < this.entities.length; i++) { this.entities[i].draw(); };
Это заменяет встроенный метод рисования игровых сущностей тем, который вы можете контролировать.
Тем не менее, прежде чем вы сможете увидеть его, вам нужно переместить файл background.png из ресурсов игры в папку мультимедиа . Результатом должен стать хороший звездный фон для вашей игры.
Добавление элементов управления клавиатурой
Даже если вы добавили объект игрока, вы не можете его переместить. Это не хорошо, так что давайте разберемся, как добавить в игру элементы управления клавиатурой.
Слушатели событий
Первый шаг — добавить прослушиватели событий, которые будут определять, когда нажимается клавиша. Добавьте следующий код в main.js и поместите его вверху функции init:
ig.input.bind(ig.KEY.LEFT_ARROW, "left"); ig.input.bind(ig.KEY.RIGHT_ARROW, "right"); ig.input.bind(ig.KEY.UP_ARROW, "up"); ig.input.bind(ig.KEY.ENTER, "play");
Должно быть достаточно очевидно, какие клавиши вы слушаете здесь; влево, вправо, вверх для перемещения игрока и введите для сброса игры, когда она закончится. Имена, данные каждому слушателю событий (влево, вправо, вверх и воспроизведение), могут быть фактически изменены на любую нужную вам строку. Тем не менее, я предлагаю вам оставить их, так как вы будете использовать их снова через мгновение.
Перемещение игрока
Весь смысл добавления элементов управления клавиатуры заключается в перемещении игрока по игре. Это удивительно просто и показывает, с какой легкостью Impact может справиться с этими проблемами.
Перейти в player.js файл объект и добавьте следующий код под свойством смещения в верхней части:
// Angle, in degrees for more rotation granularity angle: 0, // Thrust, dictating how much to accelerate thrust: 0,
И добавьте следующий код над this.parent в функцию обновления:
// "input.pressed" is called once for every key press // "input.state" is called on every frame that the key is held down for if (ig.input.state("left")) { this.angle -= 3; }; if (ig.input.state("right")) { this.angle += 3; }; if (ig.input.state("up")) { // Accelerate the player in the right direction this.accel.x = Math.sin(this.angle*Math.PI/180)*this.thrust; this.accel.y = -(Math.cos(this.angle*Math.PI/180)*this.thrust); this.currentAnim = this.anims.thrust; } else { this.accel.x = 0; this.accel.y = 0; this.currentAnim = this.anims.idle; }; // Set the angle for the current animation this.currentAnim.angle = this.angle*(Math.PI/180);
Это может выглядеть много, но это удивительно мало для добавления полного контроля над движением игрока. Позвольте мне объяснить, что происходит.
Первый условный оператор смотрит на ig.input.state («left»), что означает, что он запрашивает текущее состояние события клавиатуры, которое вы назвали «left» в предыдущем разделе (вот почему было важно не изменить эти имена). Если в данный момент эта клавиша нажата, метод ig.input.state вернет true , иначе false .
В случае левой и правой клавиш вы увеличиваете или уменьшаете свойство угла игрока в зависимости от того, какая клавиша нажимается. Затем, в случае клавиши вверх, вы используете этот угол вместе со свойством тяги, чтобы вычислить ускорение игрока под углом, которым он направляется. Это основано на достаточно стандартной тригонометрии, которую я предлагаю вам изучить, чтобы полностью понять.
Кроме того, с помощью клавиши «вверх» вы переключаете анимацию игрока на анимацию «толчка», если клавиша нажата (игрок движется), а затем возвращаетесь к анимации «холостого хода», если клавиша не нажимается. нажата (плеер все еще).
Наконец, вы используете свойство угла проигрывателя, чтобы установить угол изображения спрайта. Эта линия важна, так как в противном случае изображение не изменит угол. Сумасшедшая формула в конце — это просто преобразование угла из градусов в радианы, так как именно в этом и работает JavaScript.
Если все прошло хорошо, теперь вы сможете вращать игрока левой и правой клавишами и запускать анимацию пламени клавишей вверх.
Но почему игрок не двигается вперед? Это потому, что вы не установили для свойства thrust значение, отличное от нуля. Вернитесь в файл main.js и измените строку playerSettings следующим образом:
- var playerSettings = {thrust: 250, maxVel: {x: 300, y: 300}};
Свойство maxVel используется для ограничения скорости перемещения объекта, что очень полезно для предотвращения ускорения игрока до скорости, близкой к скорости света.
Попробуйте в браузере; Теперь вы сможете управлять ракетой по экрану с довольно реалистичной физикой ощущений. Хороший!
Завершение игры
Итак, теперь у вас есть рабочая игра, но вы еще не можете умереть, что делает ее немного бессмысленной. Хорошей новостью является то, что добавление игры поверх логики очень просто с Impact.
Откройте файл сущности player.js (последний раз, обещаю) и следующий код под свойством type вверху:
- checkAgainst: ig.Entity.TYPE.B,
Это должно иметь смысл; он устанавливает сущность игрока (типа A) для проверки объектов типа B (астероидов). Чтобы использовать это, вам нужно настроить еще одну функцию, поэтому добавьте следующий код над функцией обновления:
check: function(other) { // Game over this.health = 0; },
Функция проверки вызывается всякий раз, когда текущий объект (игрок) перекрывает другой объект того же типа, который объявлен в свойстве checkAgainst. Если две сущности перекрываются, то пришло время убить игрока, поэтому вы устанавливаете свойство здоровья на ноль. Это на самом деле не завершит игру, но вы добавите эту логику дальше.
Откройте файл main.js (последний раз) и добавьте следующий код ниже свойства background вверху:
- gameOver: false,
Вы будете использовать это свойство, чтобы сообщить игре, когда она закончится. Добавьте следующий код над this.parent в функцию обновления:
// Run if the game is over if (this.gameOver) { if(ig.input.pressed("play") ) { ig.system.setGame(MyGame); }; // Return to stop anything else updating return; };
Это остановит обновление игры, если игра закончится, и сбросит игру, используя метод ig.system.setGame, когда нажата клавиша ввода. Всего несколько строк кода могут сделать так много!
Чтобы фактически установить свойство gameOver, вы захотите добавить следующий код под this.parent в функции обновления:
// Check for game over condition if (this.player.health == 0) { this.gameOver = true; };
Если игрок мертв, игра будет завершена в следующем цикле. Просто.
Последний шаг во всей игре (почти готово) — показать сообщение, когда игра окончена. Добавьте следующий код под this.background.draw в функции draw:
// Game over screen if (this.gameOver) { this.font.draw("Game Over!", ig.system.width/2, 132, ig.Font.ALIGN.CENTER); this.font.draw("Press Enter", ig.system.width/2, 232, ig.Font.ALIGN.CENTER); this.font.draw("to Restart", ig.system.width/2, 272, ig.Font.ALIGN.CENTER); // Return to stop anything else drawing return; };
При этом используется тот же код, который был включен в код Impact в начале для отображения этого небольшого демонстрационного сообщения, за исключением того, что в этот раз вы используете его для отображения сообщения в трех отдельных строках. Однако, если вы запустите это в своем браузере, вы заметите, что шрифт абсолютно крошечный!
Чтобы исправить это, вам необходимо заменить файл 04b03.font.png в папке мультимедиа на один из игровых ресурсов, поставляемых с этим руководством, или создать собственное изображение шрифта с помощью Impact Font Tool . Результат должен быть намного больше, более читаемый шрифт.
Резюме
Созданная вами игра проста, но она показывает вам большинство основных функций, которые предоставляет движок Impact из коробки. Я бы определенно предложил продвинуть игру дальше, добавив аудио, патроны для уничтожения астероидов, улучшенную графику для астероидов и, возможно, даже сенсорное управление для мобильных устройств.
Итак, вы достигли конца этого путешествия с игровым движком Impact. Надеюсь, вы нашли его таким же интересным и захватывающим, как я это сделал в первый раз. На мой взгляд, Impact — очень мощный движок, который вы обязательно должны учитывать при разработке HTML5-игр в будущем.
Если вы хотите увидеть игру в действии, посмотрите демо-версию или загрузите исходный код и запустите его самостоятельно. Обратите внимание, что для запуска демоверсии вам понадобится лицензионная копия игрового движка Impact HTML5 .