На прошлой неделе я представил игру HTML5, известную как SeaBattle , в качестве демонстрации того, чего вы можете достичь с помощью API-интерфейсов HTML5 для аудио, холста и веб-хранилища. Затем я показал вам, как встроить эту игру в веб-страницу, и рассмотрел ее архитектуру. Эта статья начинает углубляться в эту архитектуру, фокусируясь на инициализации. Он исследует SeaBattle
объекта SeaBattle
init(width, height)
и связанные с ним функции.
Инициализация SeaBattle
В листинге 1 представлена реализация функции init(width, height)
.
init: function(width, height) { var canvas = $("<canvas width='"+width+"' height='"+height+"'></canvas>"); canvas.appendTo("body"); SeaBattle.ctx = canvas.get(0).getContext("2d"); SeaBattle.ctx.font = "30px Arial"; SeaBattle.ctx.textAlign = "center"; var seed = 5*height/6; SeaBattle.hillTops = new Array(); for (var i = 0; i < width; i++) { SeaBattle.hillTops.push(seed); var x = SeaBattle.rnd(seed); if (x < seed/4) { if (--seed < 2*height/3) seed = 2*height/3; } else if (x > 3*seed/4) { if (++seed > height-1) seed = height-1; } } SeaBattle.width = width; SeaBattle.height = height; SeaBattle.dc = new Array(SeaBattle.MAX_DC); SeaBattle.torp = new Array(SeaBattle.MAX_TORP); SeaBattle.explosion = null; SeaBattle.msg = ""; SeaBattle.score = 0; SeaBattle.hiScore = 0; if (SeaBattle.supports_html5_storage()) { var temp = localStorage.getItem("hiScore"); if (temp != undefined) SeaBattle.hiScore = temp; } SeaBattle.lives = 4; window.keydown = {}; function keyName(event) { return jQuery.hotkeys.specialKeys[event.which] || String.fromCharCode(event.which).toLowerCase(); } $(document).bind("keydown", function(event) { keydown[keyName(event)] = true; }); $(document).bind("keyup", function(event) { keydown[keyName(event)] = false; }); SeaBattle.imgTitle = new Image(); SeaBattle.imgTitle.src = "images/title.png"; SeaBattle.imgSky = new Image(); SeaBattle.imgSky.src = "images/sky.png"; SeaBattle.imgMoon = new Image(); SeaBattle.imgMoon.src = "images/moon.png"; SeaBattle.imgShipLeft = new Image(); SeaBattle.imgShipLeft.src = "images/shipLeft.png"; SeaBattle.imgShipRight = new Image(); SeaBattle.imgShipRight.src = "images/shipRight.png"; SeaBattle.imgSubLeft = new Image(); SeaBattle.imgSubLeft.src = "images/subLeft.png"; SeaBattle.imgSubRight = new Image(); SeaBattle.imgSubRight.src = "images/subRight.png"; SeaBattle.imgExplosion = new Array(); for (var i = 0; i < 17; i++) { var image = new Image(); image.src = "images/ex"+i+".png"; SeaBattle.imgExplosion.push(image); } SeaBattle.imgTorpedo = new Image(); SeaBattle.imgTorpedo.src = "images/torpedo.png"; SeaBattle.imgDC = new Image(); SeaBattle.imgDC.src = "images/dc.png"; SeaBattle.audBombLoaded = false; SeaBattle.audBomb = document.createElement("audio"); SeaBattle.audBomb.onloadeddata = new function() { SeaBattle.audBombLoaded = true; }; SeaBattle.audBomb.src = (navigator.userAgent.indexOf("MSIE") == -1) ? "audio/bomb.wav" : "audio/bomb.mp3"; SeaBattle.state = SeaBattle.STATE_INIT; }
Листинг 1. Инициализация игры включает создание / инициализацию холста и подводного ландшафта, привязку горячих клавиш, загрузку игрового ресурса и многое другое.
В листинге 1 сначала используется jQuery для создания узла элемента <canvas>
, а затем устанавливается его в дереве обозревателя Document Object Model (DOM). Это решает эту задачу следующим образом:
- Вызовите конструктор
jQuery( html )
чтобы проанализировать строкуhtml
, создать узлы DOM из проанализированного HTML и создать / вернуть объектjQuery
который ссылается на эти узлы. В листинге 1 создается один узел<canvas>
DOM. -
appendTo("body")
для этого нового объектаjQuery
чтобы присоединить проанализированные узлы DOM HTML к узлу элемента<body>
веб-страницы. В листинге 1 присоединяется узел<canvas>
узлу<body>
страницы.
Контекст холста получается через canvas.get(0).getContext("2d")
и присваивается SeaBattle
ctx
SeaBattle
. Затем свойства font
и textAlign
контекста 2D-рисования инициализируются, чтобы указать, что текст должен быть нарисован шрифтом Arial с высотой 30 пикселей, и чтобы упростить горизонтальное центрирование текста.
В листинге 1 продолжается создание подводного ландшафта путем случайного выбора вершин холмов. Крайняя левая вершина холма находится в средней точке нижней трети холста. Каждая вершина холма справа относительно предыдущей вершины холма.
Продолжая, значения width
и height
передаваемые в init(width, height)
, сохраняются в одноименных свойствах SeaBattle
чтобы к ним можно было получить доступ из других функций. Кроме того, инициализируются следующие свойства SeaBattle
:
-
dc
инициализируется в массив, который будет хранить не болееMAX_DC
объектов глубинного заряда. -
torp
инициализируется в массив, который будет хранить не болееMAX_TORP
объектов торпеды. -
explosion
инициализируетсяnull
. Функцияupdate()
проверяет это свойство, чтобы выяснить, происходит ли взрыв. Когда происходит взрыв,explosion
присваивается ссылка на объект взрыва. -
msg
инициализируется пустой строкой. Когда корабль или подводная лодка побеждает, этому свойству присваивается подходящее сообщение для последующего отображения в функцииdraw()
. -
score
обнуляется и отражает текущий счет игрока. Эта оценка появляется в верхнем левом углу холста. -
hiScore
инициализируется нулем и отражает самый высокий предыдущий результат игрока. Если текущий браузер поддерживает локальный аспект веб-хранилища HTML5, и если этот счет ранее был сохранен, дляhiScore
установлено сохраненное значение. Высокая оценка отображается в скобках после текущей оценки. -
lives
инициализируется равным четырем и отражает общее количество жизней разрушителей, которые можно прожить до окончания игры. Это число уменьшается на единицу каждый раз, когда разрушитель уничтожается.
Игры, использующие ввод с клавиатуры, обычно распознают горячие клавиши , которые являются клавишами, запускающими различные операции при нажатии. Кроме того, каждая операция обычно повторяется, пока ее горячая клавиша удерживается нажатой. Например, объект продолжает двигаться влево, пока не отпущена клавиша со стрелкой влево.
Различия в том, как браузеры интерпретируют свойства keyCode
и charCode
объекта ключевого события наряду с другими факторами, затрудняют реализацию собственной логики реагирования на горячие клавиши. Однако эта задача не должна быть слишком сложной для выполнения, как показывают следующие шаги:
- Прикрепите слушателей событий вниз и вверх к холсту, как в
canvas.onkeydown = keyDown;
иcanvas.onkeydown = keyUp;
,keyDown
иkeyUp
идентифицируют функции, которые отвечают на события нажатия клавиш вниз и вверх соответственно. - Создайте изначально пустой ассоциативный массив и назначьте его объекту
window
, как вwindow.keydown = {}
. Клавиша каждой записи будет именем клавиши, которая была нажата, и ее значение будет истинным, если клавиша нажата, или ложью, когда клавиша нажата. - Для каждого из
keyDown()
иkeyUp()
вызовите функцию, которая возвращает имя ключа, которое является либо символьным ключом, либо не символьным (специальным) ключом. Затем используйте результат в качестве индекса в массивеkeydown
. ДляkeyDown()
присвойте true этой записи массива. ДляkeyUp()
вместо этого присвойте false.
Реализация этого решения может быть неприятной. Например, charCode
всегда неопределен в Opera. Почему бы не позволить jQuery и плагину jQuery HotKeys выполнять большую часть этой работы за вас?
jQuery предлагает мощную возможность привязки, которая позволяет легко регистрировать функции обработки событий. Кроме того, плагин HotKeys позволяет возвращать имя персонажа или специальный ключ. В листинге 1 эти возможности используются для установки обработки ключевых событий, как обсуждалось ранее.
В листинге 1 теперь начинается загрузка ресурсов изображений, которые хранятся в каталоге images
, путем создания экземпляра объекта Image
и присвоения местоположения и имени изображения свойству src
объекта. Также начинается загрузка аудио ресурса, который хранится в audio
каталоге. В отличие от других браузеров, Safari не предоставляет объект Audio
. Чтобы обеспечить согласованное поведение в разных браузерах, document.createElement("audio")
используется для создания эквивалентного объекта.
Когда изображение завершает загрузку, объект Image
присваивает true своему complete
свойству. Чтобы определить, что аудиофайл завершил загрузку, onloadeddata
« Audio
» назначается функция обработчика onloadeddata
которая присваивает true SeaBattle
audBombLoaded
.
За исключением Internet Explorer, все браузеры, упомянутые в первой части этой серии, поддерживают формат WAV. Вместо этого Internet Explorer поддерживает MP3. В листинге 1 определяется, является ли текущий браузер Internet Explorer, прежде чем выбрать подходящий аудиофайл для загрузки. Выражение navigator.userAgent.indexOf("MSIE")
возвращает значение, отличное от -1, когда текущим браузером является Internet Explorer. Этот факт помогает листингу 1 выбирать между audio/bomb.wav
и audio/bomb.mp3
, которые назначаются свойству src
объекта « Audio
».
Последняя задача листинга 1 — добавить свойство state
к объекту SeaBattle
и присвоить этому свойству STATE_INIT
. Это состояние приводит к тому, что холст представляет центрированное сообщение Initializing...
пока все ресурсы игры не закончат загрузку.
Получение случайных целых
Функция init(width, height)
полагается на SeaBattle
rnd(limit)
SeaBattle
которая возвращает случайные целые числа, чтобы она могла генерировать ландшафт. В листинге 2 представлена реализация rnd(limit)
.
rnd: function(limit) { return (Math.random()*limit)|0; }
Листинг 2: Побитовые операторы заставляют JavaScript преобразовывать числа с плавающей точкой в целые числа.
В листинге 2 возвращается случайно выбранное целое число от нуля до limit - 1
. Поскольку требуется целочисленный результат, а Math.random()*limit
возвращает число с дробью, |0
используется для усечения результата до целого числа. Чтобы узнать больше о преобразовании JavaScript в целочисленные возможности, ознакомьтесь с разделом часто задаваемых вопросов по преобразованию типов Javascript . В частности, прочтите раздел ToInt32 FAQ, чтобы узнать о функции ToInt32
реализации JavaScript.
Обнаружение HTML5 локального хранилища
Функция init(width, height)
также опирается на SeaBattle
supports_html5_storage()
SeaBattle
для определения локального аспекта веб-хранилища. В листинге 3 представлена реализация supports_html5_storage()
.
supports_html5_storage: function() { try { return 'localStorage' in window && window['localStorage'] !== null && window['localStorage'] !== undefined; } catch (e) { return false; } }
Листинг 3: Старые версии Firefox вызывают исключение, когда куки отключены.
В листинге 3 обнаруживается поддержка локального аспекта веб-хранилища путем проверки объекта глобального window
на наличие свойства localStorage
. Когда это свойство существует и не является null
или undefined
, эта функция возвращает true; в противном случае возвращается false.
Вывод
Функция init(width, height)
работает с функциями rnd(limit)
и SeaBattle
supports_html5_storage()
для правильной инициализации объекта SeaBattle
. Следующим шагом в понимании игрового процесса SeaBattle является изучение функции update()
, которая является предметом третьей части этой серии. В следующую пятницу вы также узнаете, как реализован объект корабля.