Статьи

Игры: Битва в открытом море, часть 1

Веб-браузеры, поддерживающие HTML5-интерфейсы API Audio, Canvas и Web Storage, представляют собой захватывающую игровую платформу. Эти API могут быть использованы для создания интересных игр с потенциальным заработком. В качестве демонстрации, эта статья начинает серию из пяти частей, посвященную разработке игр для HTML5, нацеленную на простую игру SeaBattle . В первой части представлен SeaBattle, показано, как его встраивать в веб-страницу, и дается обзор его архитектуры. Представленная здесь игра была протестирована в настольных браузерах Chrome, Firefox, Internet Explorer 9, Opera 12 и Safari 5.

Представляем SeaBattle

Несколько лет назад я играл в игру, в которой эсминец и несколько подводных лодок вступают в бой. Поскольку в эту игру было очень весело играть, я реализовал более простую форму игры для этой серии. На рисунке 1 представлен снимок экрана с заголовком моей игры SeaBattle. Фотография эсминца была получена от Wikimedia Commons .

SeaBattle Title Screen

Рисунок 1: Экран заголовка представляет SeaBattle.

Экран заголовка рисунка 1 знакомит вас с SeaBattle и подсказывает вам нажать клавишу Return, чтобы начать эту игру. Когда вы нажимаете эту клавишу, вас приветствует экран, похожий на экран, показанный на рисунке 2.

SeaBattle Gameplay

Рисунок 2: Эсминец сражается с подводной лодкой. Глубина заряда и размеры торпеды преувеличены, чтобы улучшить видимость этих игровых объектов.

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

Где-то под вами, на сцену выходит подводная лодка и начинает стрелять торпедами. Вы можете попытаться уклониться от торпеды, используя левую и правую клавиши со стрелками. Изображение разрушителя изменяется, чтобы отразить новое направление. Его скорость меняется, когда он достигает края холста.

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

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

Встраивание SeaBattle в веб-страницу

SeaBattle состоит из JavaScript-файла SeaBattle.js который опирается на jQuery и плагин jQuery HotKeys (обсуждается во второй части этой серии статей). Чтобы встроить эту игру в веб-страницу, включите эти файлы, как показано в листинге 1.

 <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script><script type="text/javascript" language="javascript" src="jquery.hotkeys.js"></script> <script type="text/javascript" src="SeaBattle.js"></script> 

Листинг 1: SeaBattle использует три внешних файла JavaScript. SeaBattle.js должен быть включен последним. Затем вставьте элемент <script> в тело страницы, которое инициализирует SeaBattle, и многократно выполняет функцию, которая обновляет состояние игры и перерисовывает холст, чтобы отразить новое состояние. В листинге 2 показан один из способов решения этой задачи.

 <script type="text/javascript">// <![CDATA[ SeaBattle.init(800, 480); // The following function is courtesy of Opera Engineer Erik Mіller -- see // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating (function() { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelRequestAnimationFrame = window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) { var f = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16-(currTime-lastTime)); var id = window.setTimeout(function() { callback(currTime+timeToCall); }, timeToCall); lastTime = currTime+timeToCall; return id; }; window.requestAnimationFrame = f; } if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function(id) { clearTimeout(id); }; }()); (function gameloop() { SeaBattle.update(); requestAnimationFrame(gameloop); SeaBattle.draw(); })(); // ]]></script> 

Листинг 2: SeaBattle инициализирует, а затем входит в бесконечный цикл update-then-draw.

В листинге 2 сначала инициализируется предварительно созданный объект SeaBattle , вызывая его функцию init(width, height) , которая создает элемент <canvas> указанной ширины (800 пикселей) и высоты (480 пикселей), загружает игровые ресурсы и выполняет другие задания.

Затем устанавливается кросс-браузерная функция requestAnimationFrame() которая делегирует функции, специфичной для браузера. Функция браузера обеспечивает более плавную анимацию, назначая функцию обратного вызова для рисования пикселей перед вызовом следующего окна браузера.

Браузеры, которые предоставляют свои собственные функции кадров анимации запроса (такие как mozRequestAnimationFrame() Mozilla), могут автоматически mozRequestAnimationFrame() частоту кадров при переключении на другую вкладку. В конце концов, в игре нет смысла работать на максимальной скорости, когда ее вывод не виден. Однако не все браузеры поддерживают эту функцию: Internet Explorer 9 является примером. Для этих браузеров setInterval() используется для вызова функции обратного вызова. Независимо от того, какая функция вызывается, рендеринг выполняется со скоростью до 60 кадров в секунду.

Наконец, в листинге 2 указывается и вызывается gameloop() , которая определяет игровой цикл SeaBattle. Эта функция выполняет следующие задачи:

  1. Выполните SeaBattle update() SeaBattle чтобы вычислить новое состояние игры на основе пользовательского ввода и других факторов.
  2. Выполните requestAnimationFrame(gameloop) чтобы запланировать gameloop() для вызова до рисования окна браузера (если поддерживается requestAnimationFrame() ) или в следующий момент времени (через setTimeout() ).
  3. Выполните SeaBattle draw() SeaBattle чтобы перерисовать холст с обновленным игровым состоянием.

Обзор JavaScript-архитектуры SeaBattle

В какой-то момент вы захотите улучшить SeaBattle, поэтому вам нужно будет понять, как он работает. Первым шагом в получении этих знаний является понимание общей архитектуры JavaScript объекта. См. Листинг 3.

 var SeaBattle = { init: function(width, height) { }, update: function() { }, draw: function() { }, MAX_DC: 2, MAX_TORP: 15, STATE_INIT: 0, STATE_TITLE: 1, STATE_PLAY: 2, STATE_WINLOSE: 3, STATE_RESTART: 4, allResourcesLoaded: function() { }, intersects: function(r1, r2) { }, makeDepthCharge: function(bound) { }, makeExplosion: function(isShip) { }, makeShip: function(x, y, bound1, bound2) { }, makeSub: function(x, y, bound1, bound2) { }, makeTorpedo: function(bound) { }, rnd: function(limit) { }, supports_html5_storage: function() { } } 

Листинг 3: SeaBattle определяет 19 статических свойств. Дополнительные свойства динамически добавляются к этому объекту.

Глобальный объект SeaBattle листинге 3 сначала представляет открытый API, состоящий из init(width, height) , update() и draw() . Затем он представляет частный API, который определяет следующие свойства псевдоконстанты (переменная, претендующая на постоянство):

  • MAX_DC указывает максимальное количество глубинных зарядов, которые могут быть в игре в любой момент времени. Небольшая стоимость усложняет уничтожение подводной лодки и приводит к более интересным играм. Эта псевдо-константа появляется в init(width, height) , update() и draw() .
  • MAX_TORP определяет максимальное количество торпед, которые могут быть в игре в любой момент времени. Большее значение, чем количество глубинных зарядов, делает игру более интересной. Эта псевдо-константа появляется в init(width, height) , update() и draw() .
  • STATE_INIT определяет начальное состояние игры. SeaBattle загружает изображения и аудио ресурсы и отображает сообщение инициализации. Состояние изменяется на STATE_TITLE после STATE_TITLE всех ресурсов. Эта псевдо-константа появляется в init(width, height) , update() и draw() .
  • STATE_TITLE определяет состояние заголовка игры. SeaBattle отображает сообщение, указывающее вам нажать Return, чтобы начать игру. Эта псевдо-константа появляется в update() и draw() .
  • STATE_PLAY определяет игровое состояние игры. Вы взаимодействуете с игрой, нажимая клавиши со стрелками влево, вправо и пробел, пока SeaBattle остается в этом состоянии. Эта псевдо-константа появляется только в update() .
  • STATE_WINLOSE определяет состояние игры на победу / поражение. После завершения взрыва игра устанавливается в это состояние и используется для отображения сообщения о победе / поражении. Эта псевдо-константа появляется в update() и draw() .
  • STATE_RESTART определяет состояние перезапуска игры. Игра устанавливается в это состояние после того, как взрыв закончился и не осталось жизней. Он используется для отображения сообщения «Игра окончена», для обнуления счета и сброса общего количества жизней до четырех. Эта псевдо-константа появляется в update() и draw() .

Частный API также определяет следующие свойства функции:

  • allResourcesLoaded() возвращает true, когда все изображения и аудио ресурсы загружены; в противном случае возвращается false.
  • intersects(r1, r2) возвращает true, когда прямоугольник, определенный r1 пересекает прямоугольник, определенный r2 ; в противном случае возвращается false.
  • makeDepthCharge(bound) создает объект заряда глубины с указанной нижней bound . Глубинные заряды исчезают, как только достигают этой границы.
  • makeExplosion(isShip) создает взрыв, где isShip определяет, isShip ли корабль или подводная лодка.
  • makeShip(x, y, bound1, bound2) создает новый корабль, в котором местоположение центра его изображения передается по x и y , и горизонтальное движение которого ограничено bound1 слева и bound2 справа.
  • makeSub(x, y, bound1, bound2) создает новый подводный объект, в котором местоположение центра его изображения передается по x и y , и горизонтальное движение которого ограничено bound1 слева и bound2 справа.
  • makeTorpedo(bound) создает торпеду с указанной верхней bound . Торпеды исчезают, как только они достигают этой границы.
  • rnd(limit) возвращает случайное целое число от нуля до limit -1.
  • supports_html5_storage() возвращает true, когда браузер поддерживает локальный аспект веб-хранилища; в противном случае возвращается false.

Вывод

SeaBattle — это пример HTML5-игры, в которой используются API-интерфейсы Audio, Canvas и Web Storage. Теперь, когда вы познакомились с этой игрой, научились встраивать ее в веб-страницу и получили обзор архитектуры, вы готовы двигаться дальше. В следующую пятницу, часть 2 начинает эту задачу с изучения функций init(width, height) , rnd(limit) и supports_html5_storage() .