Разработка 2D-игр на HTML5: Завершающие штрихи

Важные завершающие штрихи и финальная доводка игры Snail Bait

В этом цикле статей знаток HTML5 Дэвид Джири шаг за шагом демонстрирует процесс создания 2D-видеоигры на HTML5. В этой заключительной статье цикла, рассматривается несколько важных функций игры Snail Bait и проводится некоторая финальная доводка эстетического характера. В частности, вы узнаете, как менять фон игры, выполнять тонкую настройку геймплея, сохранять счет, затенять органы управления, контролировать частоту кадров, отслеживать жизни, отображать титры, публиковать результаты игры в Твиттере и развертывать игру на сервере.

Дэвид Джири, президент, Clarity Training, Inc.

David GearyАвтор, лектор и консультант Дэвид Джири является президентом компании Clarity Training, Inc., Он обучает разработчиков создавать Web-приложения с использованием JSF и Google Web Toolkit (GWT). Он участвовал в экспертных группах JSTL 1.0 и JSF 1.0/2.0, был соавтором сертификационного экзамена Web Developer Certification Exam компании Sun, а также принимал участие в проектах с открытым кодом, в том числе Apache Struts и Apache Shale. Книга Дэвида Graphic Java Swing стала одной из самых продаваемых книг о Java, а Core JSF (в соавторстве с Кэем Хорстманом) - одна из самых продаваемых книг о JSF. Дэвид регулярно выступает на конференциях и встречах пользовательских групп. Он является регулярным участником конференций NFJS с 2003 года, проводил курсы в университете Java и дважды удостаивался звания JavaOne rock star.



06.12.2013

К концу предыдущей статьи цикла Разработка 2D-игр на HTML5 мы довели игру Snail Bait до состояния, в котором в нее уже можно играть, однако она все еще сыровата. В этой заключительной статье я показываю, как довести игру Snail Bait из этого состояния до финальной полностью отлаженной версии, показанной на рис. 1.

Рисунок 1. Финальная версия игры Snail Bait
Screen capture of the final version of Snail Bait

В этой статье вы научитесь выполнять следующие операции:

  • Заменять фоновое изображение с помощью CSS-градиентов.
  • Выполнять тонкую настройку геймплея и графики.
  • Вести счет.
  • Контролировать частоту кадров и отображать предупреждение при медленной работе игры.
  • Добавлять спецэффекты в виде встряхивания фона.
  • Отслеживать жизни.
  • Выполнять переход между жизнями.
  • Отображать титры.
  • Отправлять счет в Твиттер.
  • Развертывать игру на сервере.

В разделе Загрузка приведена ссылка на полную финальную версию кода Snail Bail.

Замена фонового изображения с помощью CSS-градиентов

В нынешнем состоянии игры Snail Bait при ее запуске загружаются три изображения: фон для веб-сайта, фон игры и лист спрайта игры. В финальной версии я уменьшаю это количество до одного изображения — я избавляюсь от фона веб-сайта и включаю фон игры в страницу спрайта. Чтобы избавиться от фона веб-сайта, я с помощью линейных CSS3-градиентов создаю фон, показанный на рис. 2:

Рисунок 2. Фон игры Snail Bait
Screen capture of the game's background

CSS-разметка для фона показана в листинге 1.

Листинг 1. Фон игры Snail Bait (фрагмент файла game.css)
body {
   /* Background from CSS3 Patterns Gallery by Anna Kassner */

   background-color: #6d6aff;

   background-image:
      repeating-linear-gradient(120deg, rgba(255,255,255,.1), rgba(255,255,255,.1) 1px, 
                                transparent 1px, transparent 60px),

      repeating-linear-gradient(60deg, rgba(255,255,255,.1), rgba(255,255,255,.1) 1px, 
                                transparent 1px, transparent 60px),

      linear-gradient(60deg, rgba(0,0,0,.1) 25%, transparent 25%, transparent 75%, 
                      rgba(0,0,0,.1) 75%, rgba(0,0,0,.1)),

      linear-gradient(120deg, rgba(0,0,0,.1) 25%, transparent 25%, transparent 75%, 
                      rgba(0,0,0,.1) 75%, rgba(0,0,0,.1));

   background-size: 70px 120px;
}
...

В галерее CSS3 Patterns Gallery размещен набор CSS-фрагментов, которые можно копировать для создания различных фонов (см. раздел Ресурсы). На рис. 3 показано несколько фонов из этой галереи.

Рисунок 3. Галерея фонов CSS3 Patterns Gallery
Screen capture of part of the CSS3 Patterns Gallery

Для игры Snail Bait я загрузил CSS-код, приведенный в листинге 1 из галереи CCC3 Patterns Gallery. Я выбрал шаблон Argyle и изменил цвет фона на оттенок синего цвета.


Тонкая настройка геймплея и графики

В процессе реализации игры ее разработчики непрерывно подстраивают ее геймплей и графику. В финальном сеансе тонкой настройки игры Snail Bat я выполняю следующие действия.

  • Изменяю расположение и размеры платформ.
  • Добавляю манипулятор отскока для всех рубинов и монет.
  • Перемещаю улитку в конец игры.
  • Реализую детонирующую кнопку.
  • Заменяю монеты.

Большинство этих изменений нетрудно увидеть на рис. 4, на котором показана улитка, бросающая бомбы при приближении игры к концу.

Рисунок 4. Бомбы улитки, уничтожающие персонажей при приближении игры к концу
Screen capture of characters eluding snail bombs near the end of Snail Bait

Примерно на середине игры бегун попадает в затруднительное положение, показанное на рис. 5. Пчела находится между бегуном и следующей платформой. Чтобы миновать пчелу и пройти этот участок игры, бегун должен нажать на синюю кнопку, которая взрывает пчелу.

Рисунок 5. Синяя кнопка для подрыва пчелы, которая охраняет следующую платформу
Screen capture of the last segment of Snail Bait

На рис. 6 показаны нажатие кнопки и взрыв пчелы. Когда спрайты взрываются, игра Snail Bait отбрасывает их, т. е. исключает их из числа кандидатов на обнаружение столкновений. Соответственно исключение взорвавшейся пчелы из числа кандидатов на обнаружение столкновений позволяет бегуну проскочить пчелу и попасть на следующую платформу.

Рисунок 6. Действие детонирующей кнопки
Screen capture from Snail Bait showing a detonating button blowing up a bee

Как и все действия игры Snail Bat, взрыв кнопки — это манипулятор. В листинге 2 показана реализация манипулятора button-detonate (взрыв кнопки).

Листинг 2. Манипулятор button-detonate
var SnailBait = function () {
   ...
   this.buttonDetonateBehavior = {
      execute: function(sprite, now, fps, lastAnimationFrameTime) {
         var BUTTON_REBOUND_DELAY = 1000;

         if ( ! sprite.detonating) { // trigger
            return;
         }

         sprite.artist.cellIndex = 1; // flatten the button

         snailBait.explode(snailBait.bees[5]);

         setTimeout( function () {
            sprite.artist.cellIndex = 0; // rebound
            sprite.detonating = false; // reset trigger
         }, BUTTON_REBOUND_DELAY);
      }
   }
};

В листинге 3 показано, каким образом в игре Snail Bait осуществляется добавление манипулятора взрыва к синей кнопке.

Листинг 3. Создание синей кнопки с манипулятором взрыва
var SnailBait = function () {

SnailBait.prototype = {
   ... 
   createButtonSprites: function () {
      var button,
         blueButtonArtist = new SpriteSheetArtist(this.spritesheet, this.blueButtonCells),
         goldButtonArtist = new SpriteSheetArtist(this.spritesheet, this.goldButtonCells);

      for (var i = 0; i < this.buttonData.length; ++i) {
         if (i === this.buttonData.length - 1) {
            button = new Sprite('button', 
                                goldButtonArtist,  
                                [ this.paceBehavior ]);
         }
         else {
            button = new Sprite('button',
                                 blueButtonArtist, 
                                 [ this.paceBehavior, 
                                   this.buttonDetonateBehavior ]);
         }

         button.width = this.BUTTON_CELLS_WIDTH;
         button.height = this.BUTTON_CELLS_HEIGHT;

         this.buttons.push(button);
      }
   }, 
};

Напоминаю, что на каждом кадре анимации игра Snail Bait перебирает все видимые спрайты. Для каждого из этих спрайтов Snail Bait обходит весь массив манипуляторов спрайта, вызывая метод execute() каждого из этих манипуляторов. Большую часть времени метод execute() манипулятора button-detonate ничего не делает, поскольку атрибут detonating этой кнопки имеет значение false. Когда Snail Bait присваивает атрибуту detonating синей кнопки значение true(см. листинг 4) манипулятор button-detonate осуществляет следующие действия:

  1. Переводит кнопку в нажатое положение.
  2. Взрывает пчелу.
  3. 3. Выдерживает паузу в одну секунду.
  4. Восстанавливает исходное изображение кнопки.
  5. Присваивает атрибуту detonating кнопки значение false.

После этого манипулятор взрыва бездействует до тех пор, пока бегун в следующий раз не столкнется с этой кнопкой.

Листинг 4. Отключение манипулятора взрыва
var SnailBait = function () {
   ...

   // The collide behavior is attached to the runner

   this.collideBehavior = {
      ...

      detonateButton: function (otherSprite) {
         otherSprite.detonating = true; // trigger
      },

      processCollision: function (sprite, otherSprite) {
         if (otherSprite.value) { 
            this.adjustScore(otherSprite);
         }

         if ('button' === otherSprite.type && sprite.falling)) {
            this.detonateButton(otherSprite);
         }
         ...
      },
   };
   ...
};

Ведение счета

Начиная с первой статьи этого цикла игра Snail Bait имеет неработающий индикатор очков. Пора сделать этот индикатор функциональным. Первый шаг состоит в присвоении значений спрайтам определенных типов. Например, в листинге 5 показан метод createRubySprites() игры, который присваивает по 100 очков за каждый рубин.

Листинг 5. Присвоение значений спрайту
SnailBait.prototype = {
   ...
   createRubySprites: function () {
      var ruby,
          rubyArtist = new SpriteSheetArtist(this.spritesheet, this.rubyCells);
   
      for (var i = 0; i < this.rubyData.length; ++i) {
         ruby = new Sprite('ruby', rubyArtist, 
                   [ new CycleBehavior(this.RUBY_SPARKLE_DURATION,
                                          this.RUBY_SPARKLE_INTERVAL),
                     new BounceBehavior(800, 600, 120) ]);

         ruby.width = this.RUBY_CELLS_WIDTH;
         ruby.height = this.RUBY_CELLS_HEIGHT;
         
         ruby.value = 100;

         this.rubies.push(ruby);
      }
   },
   ...
};

Как показано в листинге 4 при столкновении бегуна с другим спрайтом, который имеет какое-либо значение, метод adjustScore() манипулятора столкновения корректирует счет игры и обновляет HTML-элемент score. Метод adjustScore() показан в листинге 6.

Листинг 6. Корректировка счета после столкновений
var SnailBait = function () {
   ...
   // The collide behavior is attached to the runner

   this.collideBehavior = {
      ...
      adjustScore: function (otherSprite) {
         if (otherSprite.value) {
            snailBait.score += otherSprite.value;
            snailBait.score = snailBait.score < 0 ? 0 : snailBait.score;
            snailBait.scoreElement.innerHTML = snailBait.score;
         }
      },
      ...
   };
};

Контроль частоты кадров и отображение предупреждения в случае необходимости

В отличие от консольных игр, которые выполняются в жестко контролируемой среде, среда HTML5-игр весьма хаотична. Видеоплееры, системное программное обеспечение для резервного копирования и даже такие приземленные вещи, как окно терминала с полупрозрачным фоном, способны поставить на колени даже самые оптимизированные игры. Поскольку мы не можем управлять средой, в которой исполняется ваша HTML5-игра, нам необходимо контролировать частоту кадров и предупреждать игрока, когда игра функционирует с неприемлемо низкой скоростью. На рис. 7 показано предупреждение о медленной работе игры Snail Bait.

Рисунок 7. Предупреждение о медленной работе игры
Screen capture of Snail Bait's running-slowly warning

Предупреждение о медленной работе игры Snail Bait содержит сведения о текущей частоте кадров. С этой целью в процессе исполнения игра Snail Bait вставляет частоту кадров в статичный текст. В листинге 7 показан HTML-код для отображения статичного текста.

Листинг 7. HTML-код для предупреждения о медленной работе игры
<html>
   <body>
      <div id='arena'>
         ...

         <!-- Running slowly........................................-->

         <div id='running-slowly'>

            <h1>Snail Bait is running slowly</h1>

            <p id='slowly-warning'></p>
            
            <p>High-performance applications, such as video players or 
              software that backs up your computer, can slow this game down. 
              For best results, hide all other windows on your desktop and 
              close CPU- or GPU- intensive apps when you play HTML5 games.
            </p>

            <p>You should also upgrade your browser to the latest version and make 
            sure that it has hardware accelerated HTML5 Canvas. Any version of Chrome 
            starting with version 18, for example, has hardware accelerated
               Canvas. Here is a link where you can download the 
               <a href='http://www.google.com/chrome/'>latest version of Chrome</a>.
            </p>

            <a id='slowly-okay' href='#'>
               Okay
            </a>

            <a  id='slowly-dont-show' href='#'>
               Do not show this warning again
            </a>
         </div>
         ...
      </div>
   </div>
</div>

В листинге 8 показано, как игра Snail Bait выводит и скрывает предупреждение о медленной работе.

Листинг 8. Вывод и скрытие предупреждения о медленной работе
SnailBait.prototype = {
   ...
   revealRunningSlowlyWarning: function (now, averageSpeed) {
      this.slowlyWarningElement.innerHTML =
      "Snail Bait is running at " +
      "<font color='red'> averageSpeed.toFixed(0)" + "</font>" +
      " frames/second (fps), but it needs more than " +
      this.runningSlowlyThreshold + " fps for the game to work correctly."

      this.runningSlowlyElement.style.display = 'block';

      setTimeout( function () {
         snailBait.runningSlowlyElement.style.opacity = 1.0;
      }, this.SHORT_DELAY);

      this.lastSlowWarningTime = now;
   },  

   hideRunningSlowlyWarning: function () {
      snailBait.runningSlowlyElement.style.display = 'none'; 
      snailBait.runningSlowlyElement.style.opacity = 0;
   },
   ...
};

Когда игра Snail Bait показывает предупреждение о медленной работе, она создает текст этого предупреждения и присваивает этот текст элементу параграфа slowly-warning. Затем она присваивает атрибуту display этого элемента значение block, чтобы браузер отображал этот элемент. Однако этот элемент первоначально является прозрачным, поэтому после короткой задержки метод revealRunningSlowlyWarning() присваивает атрибуту opacity (степень непрозрачности) этого элемента значение 1.0, делая его непрозрачным. Эта настройка инициирует CSS-переход, который за одну секунду постепенно делает этот прозрачный элемент непрозрачным. В листинге 9 показан CSS-код для элемента running-slowly.

Листинг 9. CSS-код для элемента running-slowly
#running-slowly {
   position: absolute;
   width: 600px;
   margin-top: 85px;
   margin-left: 90px;
   text-align: center;
   background: rgba(255,255,255,0.85);
   text-align: left;
   padding: 0px 20px 20px 20px;
   color: navy;
   text-shadow: 1px 1px 1px rgba(255,255,255,0.5);

   -webkit-transition: opacity 1s;
   -moz-transition: opacity 1s;
   -o-transition: opacity 1s;
   transition: opacity 1s;
   
   -webkit-box-shadow: rgba(0,0,0,0.5) 4px 4px 8px;
   -moz-box-shadow: rgba(0,0,0,0.5) 4px 4px 8px;
   -o-box-shadow: rgba(0,0,0,0.5) 4px 4px 8px;
   box-shadow: rgba(0,0,0,0.5) 4px 4px 8px;

   opacity: 0;
   display: none;

   z-index: 2;
}

В процессе своего исполнения игра Snail Bait контролирует частоту кадров (см. листинг 10).

Листинг 10. Контроль частоты кадров
SnailBait.prototype = {
   ...
   animate: function (now) { 
      if (snailBait.paused) {
         setTimeout( function () {
            requestNextAnimationFrame(snailBait.animate);
         }, snailBait.PAUSED_CHECK_INTERVAL);
      }
      else {
         snailBait.fps = snailBait.calculateFps(now); 

         if (snailBait.windowHasFocus && !snailBait.paused &&
             snailBait.showSlowWarning &&
             now - snailBait.lastSlowWarningTime > 
             snailBait.FPS_SLOW_CHECK_INTERVAL) {
            snailBait.checkFps(now); 
         }

         snailBait.draw(now);
         requestNextAnimationFrame(snailBait.animate);
      }
   },

   checkFps: function (now) {
      var averageSpeed;

      this.updateSpeedSamples(snailBait.fps);

      averageSpeed = this.calculateAverageSpeed();

      if (averageSpeed < this.runningSlowlyThreshold) {
         this.revealRunningSlowlyWarning(now, averageSpeed);
      }
   },
   ...
};

Каждые FPS_SLOW_CHECK_INTERVAL екунд (в нашем случае этому параметру присвоено значение 4 секунды) игра Snail Bait проверяет частоту кадров, обновляя массив отсчетов скорости и вычисляя среднюю скорость. Если средняя скорость оказывается ниже установленного порога, игра Snail Bait отображает сообщение о медленной работе. В листинге 11 показаны методы Snail Bat для обновления отсчетов скорости и вычисления средней скорости:

Листинг 11. Обновление отсчетов скорости и вычисление средней скорости
SnailBait.prototype = {
   ...
   updateSpeedSamples: function (fps) {
      this.speedSamples[this.speedSamplesIndex] = fps;

      if (this.speedSamplesIndex !== this.NUM_SPEED_SAMPLES-1) {
         this.speedSamplesIndex++;
      }
      else {
         this.speedSamplesIndex = 0;
      }
   },

   calculateAverageSpeed: function () {
      var i,
      total = 0;

      for (i=0; i < this.NUM_SPEED_SAMPLES; i++) {
         total += this.speedSamples[i];
      }

      return total/this.NUM_SPEED_SAMPLES;
   },
   ...
};

Когда игрок нажимает кнопку OK на предупреждении о медленной работе, игра Snail Bait скрывает это предупреждение и сбрасывает отсчеты скорости. Когда игрок нажимает кнопку Do not show this warning again (Больше не показывать это предупреждение), игра Snail Bait скрывает это предупреждение и устанавливает признак, отключающий контроль частоты кадров. В листинге 12 показан код для этих двух обработчиков событий.

Листинг 12. Обработчики событий медленной работы
snailBait.slowlyDontShowElement.onclick = function (e) {
   snailBait.hideRunningSlowlyWarning();
   snailBait.showSlowWarning = false;
};


snailBait.slowlyOkayElement.onclick = function (e) {
   snailBait.hideRunningSlowlyWarning();
   snailBait.speedSamples = [60,60,60,60,60,60,60,60,60,60]; // reset
};

Добавление спецэффектов

Финальная версия игры Snail Bat встряхивает фон игры, когда бегун сталкивается с другим спрайтом и взрывается. В листинге 13 показан фрагмент манипулятора столкновения бегуна, в котором производится вызов метода shake()M игры Snail Bait, когда такое столкновение происходит.

Листинг 13. Сотрясение фона
var SnailBait = function () {
   ...
   // The collide behavior is attached to the runner

   this.collideBehavior = {
      ...
      processCollision: function (sprite, otherSprite) {
         ...
         if ('bat' === otherSprite.type || 'bee' === otherSprite.type   ||
         'snail' === otherSprite.type || 'snail bomb' === otherSprite.type) {
            snailBait.explode(sprite);

            snailBait.shake();

            setTimeout( function () {
               snailBait.loseLife();
               snailBait.reset();
               snailBait.fadeAndRestoreCanvas();
            }, snailBait.EXPLOSION_DURATION);
         }
      },
   };
   ...
};

Метод shake() игры Snail Bait состоит из серий вложенных вызовов метода setTimeout() (см. листинг 14).

Листинг 14. Реализация метода shake()
SnailBait.prototype = function () {
   ... 
   shake: function () {
      var SHAKE_INTERVAL = 90, // milliseconds
          v = snailBait.BACKGROUND_VELOCITY,
          ov = snailBait.bgVelocity; // ov means original velocity
   
      this.bgVelocity = -this.BACKGROUND_VELOCITY;

      setTimeout( function (e) {
       snailBait.bgVelocity = v;
       setTimeout( function (e) {
          snailBait.bgVelocity = -v;
          setTimeout( function (e) {
             snailBait.bgVelocity = v;
             setTimeout( function (e) {
                snailBait.bgVelocity = -v;
                setTimeout( function (e) {
                   snailBait.bgVelocity = v;
                   setTimeout( function (e) {
                      snailBait.bgVelocity = -v;
                      setTimeout( function (e) {
                         snailBait.bgVelocity = v;
                         setTimeout( function (e) {
                            snailBait.bgVelocity = -v;
                            setTimeout( function (e) {
                               snailBait.bgVelocity = v;
                               setTimeout( function (e) {
                                  snailBait.bgVelocity = -v;
                                  setTimeout( function (e) {
                                     snailBait.bgVelocity = v;
                                     setTimeout( function (e) {
                                        snailBait.bgVelocity = ov;
                                     }, SHAKE_INTERVAL);
                                  }, SHAKE_INTERVAL);
                               }, SHAKE_INTERVAL);
                            }, SHAKE_INTERVAL);
                         }, SHAKE_INTERVAL);
                      }, SHAKE_INTERVAL);
                   }, SHAKE_INTERVAL);
                }, SHAKE_INTERVAL);
             }, SHAKE_INTERVAL);
          }, SHAKE_INTERVAL);
       }, SHAKE_INTERVAL);
     }, SHAKE_INTERVAL);
   },
   ...
};

Каждые 90 мс метод shake()игры Snail Bait инвертирует вектор скорости фона, что порождает иллюзию дрожания игры.


Отслеживание жизней

Финальная версия Snail Bat дает игроку три жизни в начале игры. Когда бегун сталкивается с враждебным спрайтом и взрывается, игрок теряет одну жизнь, а бегун возвращается в начало игры.

Количество оставшихся жизней отображается в Snail Bait над холстом игры в виде миниатюрных значков с изображением бегуна. Эти изображения специфицированы в HTML-коде игры (см. листинг 15).

Листинг 15. Элементы lives
<html>
   ...
   <body>
      <div id='arena'>
         <div id='header'>
            <div id='score'>0</div>

            <div id='lives'>
               <img id='life-icon-left'   src='images/runner-small.png'/>
               <img id='life-icon-middle' src='images/runner-small.png'/>
               <img id='life-icon-right'  src='images/runner-small.png'/>
            </div>
         </div>
         ...
      </div>
      ...
   <body>
</html>

В процессе исполнения игры метод loseLife()— вызов которого осуществляется при столкновении бегуна с враждебным спрайтом (см. листинг 13) — обновляет элемент lives. В листинге 16 показаны функция updateLivesElement и методы loseLife():

Листинг 16. Обновление элемента lives
SnailBait.prototype = {
   ...

   updateLivesElement: function () {
      if (this.lives === 3) {
         this.lifeIconLeft.style.opacity   = 1.0;
         this.lifeIconMiddle.style.opacity = 1.0;
         this.lifeIconRight.style.opacity  = 1.0;
      }
      else if (this.lives === 2) {

         this.lifeIconLeft.style.opacity   = 1.0;
         this.lifeIconMiddle.style.opacity = 1.0;
         this.lifeIconRight.style.opacity  = 0;
      }
      else if (this.lives === 1) {
         this.lifeIconLeft.style.opacity   = 1.0;
         this.lifeIconMiddle.style.opacity = 0;
         this.lifeIconRight.style.opacity  = 0;
      }
      else if (this.lives === 0) {
         this.lifeIconLeft.style.opacity   = 0;
         this.lifeIconMiddle.style.opacity = 0;
         this.lifeIconRight.style.opacity  = 0;
      }
   },

   loseLife: function () {
      this.lives--;
      this.updateLivesElement();

      if (this.lives === 1) {
         snailBait.revealToast('Last chance!');
      }

      if (this.lives === 0) {
         this.gameOver();
      }
   },
   ...
};

Переход между жизнями

Когда игрок теряет жизнь, игра Snail Bait, помимо уменьшения количества значков, представляющих жизни, постепенно уменьшает непрозрачность холста до тех пор, пока он не станет почти полностью прозрачным (рис. 8). Затем игра постепенно восстанавливает непрозрачность холста. Кроме того, игра Snail Bait сбрасывает бегуна с третьей (самой верхней) дорожки, чтобы показать, что началась новая жизнь бегуна.

Рисунок 8. Постепенно повышение прозрачности холста при переходе игры между жизнями
Screen capture from Snail Bait showing a transition between lives

В листинге 17 показан метод reset() игры Snail Bait, вызов которого осуществляется из манипулятора столкновения бегуна (см. листинг 13).

Листинг 17. Снижение и восстановление до прежнего уровня степени непрозрачности холста
SnailBait.prototype = {
   ...
   fadeAndRestoreCanvas: function () {
      snailBait.canvas.style.opacity = 0.2;

      setTimeout( function () {
         snailBait.canvas.style.opacity = 1.0;
      }, 2500);
   },

   resetRunner: function () {
      snailBait.runner.exploding = false; 
      snailBait.runner.visible = false;
      snailBait.runner.opacity = snailBait.OPAQUE;
      snailBait.runner.artist.cells = snailBait.runnerCellsRight;

      if (snailBait.runner.jumping) { snailBait.runner.stopJumping(); }
      if (snailBait.runner.falling) { snailBait.runner.stopFalling(); }
   },

   reset: function () {
      var CANVAS_TRANSITION_DURATION = 2000,
          CONTINUE_RUNNING_DURATION = 1000;

      this.resetRunner();

      setTimeout( function () {
         snailBait.backgroundOffset = 
            snailBait.INITIAL_BACKGROUND_OFFSET;

         snailBait.spriteOffset = snailBait.INITIAL_BACKGROUND_OFFSET;
         snailBait.bgVelocity = snailBait.INITIAL_BACKGROUND_VELOCITY;

         snailBait.runner.track = 3;
         snailBait.runner.top = snailBait.calculatePlatformTop(snailBait.runner.track) - 
                                snailBait.runner.height;

         for (var i=0; i < snailBait.sprites.length; ++i) { 
            snailBait.sprites[i].visible = true;
         }

         setTimeout( function () {
            snailBait.runner.runAnimationRate = 0; // stop running
         }, CONTINUE_RUNNING_DURATION);
      }, CANVAS_TRANSITION_DURATION);
   },
   ...
};

Метод reset() перезапускает игру и готовит ее к новой жизни следующим образом.

  1. Восстанавливает исходные значения смещений спрайтов и фона.
  2. Размещает бегуна на третьей дорожке.
  3. Активирует видимое состояние всех спрайтов игры.

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


Отображение титров

В конце игры Snail Bait отображаются титры (см. рис. 9).

Рисунок 9. Отображение титров в конце игры
Screen capture of Snail Bait displaying game credits

В листинге 18 показаны методы Snail Bait, участвующие в выводе и скрытии титров.

Листинг 18. Вывод и скрытие титров
SnailBait.prototype = {
   ...
   
   gameOver: function () {
      snailBait.revealCredits();
   },

   restartGame: function () {
      this.hideCredits();

      this.lives = this.MAX_NUMBER_OF_LIVES;
      this.updateLivesElement();

      this.score = 0;
      this.updateScoreElement();
   },

   revealCredits: function () {
      this.creditsElement.style.display = 'block';
      this.revealLivesIcons();

      this.tweetElement.href = TWEET_PREAMBLE + this.score + TWEET_PROLOGUE;

      setTimeout( function () {
         snailBait.creditsElement.style.opacity = 1.0;
      }, snailBait.SHORT_DELAY);
   },

   hideCredits: function () {

      var CREDITS_REVEAL_DELAY = 2000;

      this.creditsElement.style.opacity = this.TRANSPARENT;

      setTimeout( function (e) {
         snailBait.creditsElement.style.display = 'none';
      }, this.CREDITS_REVEAL_DELAY);
   }, 
};

Метод gameOver()игры Snail Bait, который вызывается методом loseLife() (см. листинг 16), выводит титры, а метод restartGame() скрывает титры. Обработчик событий, связанный со ссылкой Play again (Играть снова) на экране титров, вызывает метод restartGame(). Этот обработчик событий показан в листинге 19.

Листинг 19. Обработчик событий для экрана титров
 snailBait.newGameLink.onclick = function (e) {
   snailBait.restartGame();
};

Отправка счета в Твиттер

Многие HTML5-игры имеют социальные аспекты, такие как публикация счета в Твиттере или взаимодействие с другими игроками в процессе игры. В конце игры Snail Bait игрок может отправить свой счет в Твиттер, для чего ему следует нажать на ссылку Tweet my score на экране с титрами (см. рис. 9). Браузер игрока открывает в новой вкладке Твиттер, после чего игра Snail Bait автоматически создает готовый к отсылке твит с объявлением счета. См. пример на рис. 10.

Рисунок 10. Создание твита со счетом
Screen capture from Twitter of tweeting a Snail Bait score

Для отправки счета в Твиттер игра Snail Bait конструирует URL-строку и присваивает эту строку атрибуту href ссылки Tweet my score (см. листинг 20).

Листинг 20. Отправка счета в Твиттер
SnailBait = function () {
   ...
   this.TWEET_PREAMBLE = 'https://twitter.com/intent/tweet?text=I scored ';
   this.TWEET_PROLOGUE = ' playing this HTML5 Canvas platformer: ' +
                         'http://bit.ly/NDV761 &hashtags=html5';
   ...
};

SnailBait.prototype = {
   ...
   
   revealCredits: function () {
      this.creditsElement.style.display = 'block';
      this.revealLivesIcons();

      this.tweetElement.href = TWEET_PREAMBLE + this.score + TWEET_PROLOGUE;

      setTimeout( function () {
         snailBait.creditsElement.style.opacity = 1.0;
      }, snailBait.SHORT_DELAY);
   },
   ...
};

Развертывание на сервере

Развертывание HTML5-игры на сервере сопряжено с двумя основными трудностями: объемом данных, которые необходимо передать от клиента к серверу при запуске игры, и количеством циклов обмена, которые необходимо осуществить между клиентом и сервером. В идеальном случае передаются минимально возможные объемы данных и генерируется минимально возможное количество HTTP-запросов. С этой целью в игре Snail Bait принимаются три меры.

  1. Сжатие JavaScript-файлов.
  2. Копирование всех JavaScript-файлов в один файл (all.js).
  3. Архивация кода, написанного на JavaScript, CSS и HTML.

Сначала простой скрипт оболочки находит все JavaScript-файлы игры Snail Bait и сжимает каждый из них с помощью утилиты YUI compressor (за дополнительной информацией обращайтесь в раздел Ресурсы) (см. листинг 21).

Листинг 21. Скрипт оболочки для сжатия JavaScript-файлов игры Snail Bait
#!/bin/bash

mkdir tmp

for f in `find . -name "*.js"`
   do
      echo 'Compressing ' $f
         java -jar ~/Utilities/yuicompressor-2.4.7.jar $f >> tmp/all.js
   done

Затем сжатые файлы объединяются в один JavaScript-файл с именем all.js, который включается в HTML-файл игры Snail Bait.

И, наконец, другой скрипт оболочки, показанный в листинге 22, осуществляет развертывание игры. С этой целью он исполняет скрипт сжатия, показанный в листинге 21, а затем архивирует код игры, написанный на JavaScript, HTML и CSS.

Листинг 22. Скрипт оболочки для развертывания игры Snail Bait
#!/bin/bash

rm -rf tmp
rm all.js
rm *.gz

echo 'Compressing...'
echo
./compress.sh

echo 'Deploying...'
echo

gzip < all.js > all.js.gz
gzip < index.html > index.html.gz
gzip < snailbait.css > snailbait.css.gz

rm -rf tmp

Сервер нужно сконфигурировать на принятие ZIP-файлов, чтобы он смог использовать архивированные файлы вместо их оригиналов. Подробности конфигурирования зависят от конкретного сервера.


Заключение

Эта статья охватила множество тем, как и весь цикл в целом. Вы увидели, как игра Snail Bait развивалась от достаточно скромной начальной версии из первой статьи—— когда она просто отображала фон, бегуна и платформы — до законченной игры. Вы научились использовать интерфейс 2D Canvas API наряду с другими API-интерфейсами HTML5, такими как requestAnimationFrame, для реализации прокрутки фонов и параллакса. Вы реализовали спрайты и их манипуляторы. Вы также научились манипулировать временем с целью реализации нелинейного движения, обнаруживать столкновения и анимировать спрайты. Однако разработка 2D-игр включает в себя множество других аспектов, которые я не описывал в этом цикле, например:

  • Реализация системы времени, позволяющей менять течение времени на всем протяжении игры.
  • Сохранение на сервере лучших итоговых результатов, а также текущих показателей игры в реальном времени.
  • Создание "потайной двери", которая предоставляет разработчикам доступ к специальным возможностям.
  • Имитация дыма и огня с помощью системы частиц.
  • Тонкая настройка игры для мобильных устройств.

Я рекомендую вам изучить все эти темы, а затем приступить к разработке своих собственных 2D-игр на HTML5.


Загрузка

ОписаниеИмяРазмер
Демонстрационный программный кодwa-html5-game10-code.zip12.9 МБ

Ресурсы

Научиться

  • Оригинал статьи: HTML5 2D game development: Wrapping up the game.
  • Core HTML5 Canvas: (David Geary, Prentice Hall, 2012): в книге Дэвида Джири приведен подробный обзор API Canvas и методов разработки игр. Посетите также сопутствующий веб-сайт и блог. .
  • Snail Bait: игра Snail Bait работает в онлайновом режиме в любом браузере с поддержкой HTML5 (лучше всего подойдет Chrome версии 18 и выше).
  • YUI Compressor: Информация о YUI Compressor.
  • Mind-blowing apps with HTML5 Canvas (Умопомрачительные приложения с применением HTML5 Canvas): выступление Дэвида Джири на конференции Strange Loop 2011.
  • HTML5 Game Development (Разработка HTML5-игр): выступление Дэвида Гири на конференции норвежских разработчиков NDC-2011.
  • Platform games статья в Википедии об играх-платформерах.
  • Side-scroller video games статья в Википедии об играх-сайдскроллерах.
  • Strategy статья в Википедии об этом шаблоне проектирования.
  • Основы HTML5: изучите основы HTML5 в рамках учебной программы developerWorks.
  • Раздел для веб-разработчиков на ресурсе developerWorks. статьи по различным веб-решениям. Читайте также материалы по веб-разработке в технической библиотеке: обширный ассортимент технических статей, рекомендаций, руководств, учебных пособий и стандартов, а также руководств серии IBM Redbook®.

Получить продукты и технологии

  • CSS3 patterns gallery(Галерея CSS3-шаблонов): созданная Леа Веру галерея с возможностью загрузки и добавления шаблонов.
  • Replica Island: загрузите открытый исходный код популярного платформера для Android. Большая часть спрайтов Snail Bait взята из Replica Island (используется с разрешения).

Обсудить

  • Присоединяйтесь к сообществу developerWorks. Связывайтесь с другими пользователями developerWorks и знакомьтесь с ориентированными на разработчиков форумами, блогами, группами и вики-ресурсами.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура
ArticleID=956412
ArticleTitle=Разработка 2D-игр на HTML5: Завершающие штрихи
publish-date=12062013