Веб-браузеры, поддерживающие HTML5-интерфейсы API Audio, Canvas и Web Storage, представляют собой захватывающую игровую платформу. Эти API могут быть использованы для создания интересных игр с потенциальным заработком. В качестве демонстрации, эта статья начинает серию из пяти частей, посвященную разработке игр для HTML5, нацеленную на простую игру SeaBattle . В первой части представлен SeaBattle, показано, как его встраивать в веб-страницу, и дается обзор его архитектуры. Представленная здесь игра была протестирована в настольных браузерах Chrome, Firefox, Internet Explorer 9, Opera 12 и Safari 5.
Представляем SeaBattle
Несколько лет назад я играл в игру, в которой эсминец и несколько подводных лодок вступают в бой. Поскольку в эту игру было очень весело играть, я реализовал более простую форму игры для этой серии. На рисунке 1 представлен снимок экрана с заголовком моей игры SeaBattle. Фотография эсминца была получена от Wikimedia Commons .
Экран заголовка рисунка 1 знакомит вас с SeaBattle и подсказывает вам нажать клавишу Return, чтобы начать эту игру. Когда вы нажимаете эту клавишу, вас приветствует экран, похожий на экран, показанный на рисунке 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. Эта функция выполняет следующие задачи:
- Выполните
SeaBattle
update()
SeaBattle
чтобы вычислить новое состояние игры на основе пользовательского ввода и других факторов. - Выполните
requestAnimationFrame(gameloop)
чтобы запланироватьgameloop()
для вызова до рисования окна браузера (если поддерживаетсяrequestAnimationFrame()
) или в следующий момент времени (черезsetTimeout()
). - Выполните
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()
.