Статьи

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

SeaBattle – это игра HTML5, которая демонстрирует полезность API-интерфейсов HTML5 для аудио, Canvas и веб-хранилищ. Эта статья завершает нашу серию игр, состоящую из пяти частей, посвященную SeaBattle, путем изучения ее функций draw() и allResourcesLoaded() . Он также рассматривает эти API, обсуждая усовершенствования игр и исследуя SeaBattle в мобильном контексте.

Рисование сцены

SeaBattle draw() объекта SeaBattle , показанная в листинге 1, вызывается для рисования игровой сцены на основе обновленного состояния.

 draw: function() { if (SeaBattle.state == SeaBattle.STATE_INIT) if (!SeaBattle.allResourcesLoaded()) { SeaBattle.ctx.fillStyle = "#000"; SeaBattle.ctx.fillRect(0, 0, SeaBattle.width, SeaBattle.height); SeaBattle.ctx.fillStyle = "#fff"; SeaBattle.ctx.fillText("Initializing...", SeaBattle.width/2, SeaBattle.height/2); return; } else SeaBattle.state = SeaBattle.STATE_TITLE; if (SeaBattle.state == SeaBattle.STATE_TITLE) { SeaBattle.ctx.drawImage(SeaBattle.imgTitle, 0, 0); return; } SeaBattle.ctx.drawImage(SeaBattle.imgSky, 0, 0); SeaBattle.ctx.fillStyle = "#404040" SeaBattle.ctx.fillRect(0, SeaBattle.height/3, SeaBattle.width, 2*SeaBattle.height/3); SeaBattle.ctx.drawImage(SeaBattle.imgMoon, SeaBattle.width-65, 25); SeaBattle.ctx.strokeStyle = "rgb(255, 102, 0)"; // orange for (var i = 0; i < SeaBattle.width; i++) { SeaBattle.ctx.beginPath(); SeaBattle.ctx.moveTo(i, SeaBattle.hillTops[i]); SeaBattle.ctx.lineTo(i, SeaBattle.height); SeaBattle.ctx.stroke(); } for (var i = 0; i < SeaBattle.MAX_DC; i++) if (SeaBattle.dc[i] != null) SeaBattle.dc[i].draw(); for (var i = 0; i < SeaBattle.MAX_TORP; i++) if (SeaBattle.torp[i] != null) SeaBattle.torp[i].draw(); if ((SeaBattle.ship != null && SeaBattle.explosion == null) || (SeaBattle.explosion != null && !SeaBattle.ship.exploded)) SeaBattle.ship.draw(); if ((SeaBattle.sub != null && SeaBattle.explosion == null) || (SeaBattle.explosion != null && !SeaBattle.sub.exploded)) SeaBattle.sub.draw(); if (SeaBattle.explosion != null) SeaBattle.explosion.draw(); SeaBattle.ctx.fillStyle = "rgba(0, 0, 255, 0.1)"; SeaBattle.ctx.fillRect(0, SeaBattle.height/3, SeaBattle.width, SeaBattle.height); SeaBattle.ctx.fillStyle = "#fff"; var align = SeaBattle.ctx.textAlign; SeaBattle.ctx.textAlign = "left"; SeaBattle.ctx.fillText("Score: "+SeaBattle.score+"("+SeaBattle.hiScore+")", 10, 45); SeaBattle.ctx.textAlign = align; for (var i = 0; i < SeaBattle.lives-1; i++) { var x = SeaBattle.width-(i+1)*(SeaBattle.imgShipLeft.width+10); var y = SeaBattle.height-SeaBattle.imgShipLeft.height; SeaBattle.ctx.drawImage(SeaBattle.imgShipLeft, x, y); } if (SeaBattle.state == SeaBattle.STATE_WINLOSE || SeaBattle.state == SeaBattle.STATE_RESTART) { SeaBattle.ctx.fillStyle = "#fff"; SeaBattle.ctx.fillText(SeaBattle.msg, SeaBattle.width/2, SeaBattle.height/2); } } 

Листинг 1: Экран инициализации отображается до тех пор, пока все ресурсы игры не загрузятся

В листинге 1 сначала определяется, находится ли игра в состоянии инициализации. Если это так, и если не все игровые ресурсы загружены, отображается экран инициализации «белое на черном». После загрузки всех игровых ресурсов состояние возвращается в состояние заголовка и отображается экран заголовка.

Если игра не инициализирует или не представляет заголовок, листинг 1 перерисовывает текущую сцену на основе текущего состояния игры. Он рисует глубинные заряды и торпеды, прежде чем рисовать корабль и подводную лодку, чтобы их выход из любого игрового объекта выглядел более естественным.

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

После отрисовки неба, воды, луны, подводного ландшафта и игровых объектов в листинге 1 отображаются текущие и высокие баллы, а также оставшиеся жизни. Наконец, он рисует сообщение по центру сцены, но только тогда, когда игра находится в состоянии выиграть / проиграть или перезапустить.

Обнаружение всех ресурсов, загруженных

Функция draw() опирается на SeaBattle allResourcesLoaded() чтобы сообщить ей, когда все ресурсы изображений и аудиоигр закончили загрузку. Нет смысла продолжать, пока все эти ресурсы не будут доступны. В листинге 2 представлена ​​реализация этой функции.

 allResourcesLoaded: function() { var status = SeaBattle.imgTitle.complete && SeaBattle.imgSky.complete && SeaBattle.imgMoon.complete && SeaBattle.imgShipLeft.complete && SeaBattle.imgShipRight.complete && SeaBattle.imgSubLeft.complete && SeaBattle.imgSubRight.complete; for (var i = 0; i < SeaBattle.imgExplosion.length; i++) status = status && SeaBattle.imgExplosion[i].complete; status = status && SeaBattle.audBombLoaded; return status; } 

Листинг 2: Логические значения свойств complete и audBombLoaded объединяются.

В листинге 2 объединяется логическое значение true / false каждого свойства complete объекта Image со значением Boolean true / false SeaBattle audBombLoaded чтобы получить результат, указывающий, загружены ли все игровые ресурсы.

Обзор HTML5-интерфейсов API Audio, Canvas и Web Storage

SeaBattle не существовал бы как есть без доступа к API-интерфейсам HTML5 Audio, Canvas и Web Storage. В этом разделе кратко рассматриваются Audio, Canvas и Web Storage для новичков, которые хотят полностью понять эту игру, но не имеют полного понимания этих API.

Просмотр аудио API

Аудиоэлемент HTML5 позволяет вам представлять звук или аудиопоток. Вы можете программно создавать и управлять экземплярами этого элемента с помощью конструктора Audio() . К сожалению, Safari не поддерживает этот конструктор, поэтому вместо него я использовал функцию createElement() DOM.

Результирующий экземпляр имеет тип HTMLAudioElement , чья функция play() используется для запуска воспроизведения звукового эффекта взрыва. Эта функция создает исключение в браузере Safari, если QuickTime не установлен. HTMLAudioElement также предоставляет onloadeddata события onloadeddata который я использую для получения уведомления о загрузке аудиоданных и присваивания true SeaBattle audBombLoaded .

Просмотр Canvas API

Элемент canvas в HTML5 позволяет выделить прямоугольную область веб-страницы и рисовать в этой области. Вы можете программно создавать экземпляры этого элемента и манипулировать ими с помощью Canvas API.

В отличие от Audio API, здесь нет конструкторов для программного создания экземпляра canvas. Вместо этого вы работаете с функцией createElement() DOM, как продемонстрировано в SeaBattle init(width, height) SeaBattle . Полученный экземпляр имеет тип HTMLCanvasElement . Прежде чем вы сможете рисовать на холсте, вам нужно получить контекст рисования. Вы можете получить этот контекст, вызвав функцию getContext() .

Обычно вы вызываете эту функцию с аргументом "2D" или "2d" для возврата 2D-контекста. CanvasRenderingContext2D объект для 2D-контекста имеет тип CanvasRenderingContext2D . CanvasRenderingContext2D объявляет различные функциональные и нефункциональные атрибуты. Функция init(width, height) демонстрирует атрибуты font и textAlign . Дополнительные атрибуты были продемонстрированы ранее в этой статье.

Просмотр API веб-хранилища

Веб-хранилище обеспечивает постоянное хранение данных пары ключ-значение в браузерах и других веб-клиентах. SeaBattle полагается на локальное хранилище, чтобы сохранить высокий балл и получить его в следующий раз при запуске игры. Высокая оценка не сохраняется, когда окно браузера закрывается, потому что Opera не обеспечивает надежного средства обнаружения событий закрытия окна.

Обнаружение поддержки локального хранилища сводится к тому, что сначала нужно проверить наличие свойства localStorage объекте глобального window , а затем убедиться, что значение этого свойства не является ни null ни undefined .

Internet Explorer 9 не поддерживает локальное хранилище для локальных файлов. Для получения дополнительной информации см. Локальное хранилище stackoverflow в IE9, происходит сбой при обращении к веб-сайту непосредственно из раздела файловой системы .

Свойство localStorage в конечном итоге имеет тип Storage . Вызовите функцию void setItem(DOMString key, DOMString value) этого свойства, чтобы сохранить пару ключ-значение, и его DOMString getItem(DOMString key) чтобы вернуть значение указанного ключа.

Каждый браузер имеет собственное локальное хранилище. Это означает, что определенный высокий балл, сохраненный одним браузером, не будет получен другим браузером. Например, можно получить высокий балл 500 в Firefox и 300 в Opera.

Улучшение SeaBattle

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

Рассмотрим следующую выдержку из объекта торпеды:

 this.move = function move() { this.y--; if (this.y < this.bound) return false; return true; } 

Чтобы заставить торпеду двигаться быстрее, просто this.y на большее значение; например, this.y -= 2; ,

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

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

Going Mobile

Размышляя об усовершенствованиях SeaBattle, нужно учитывать одно важное улучшение. Игра должна быть протестирована на браузерах мобильных устройств. Если вы надеетесь монетизировать свои HTML5-игры, вы не можете ограничивать их браузерами для настольных компьютеров. Возможно, вы сначала протестируете свою игру на платформах iOS и Android.

Ранее установив эмулятор Android 4.1, я решил протестировать SeaBattle в браузерном приложении по умолчанию. Моей первой заботой была возможность увидеть полотно целиком. Оказывается, это не проблема, как показано на рисунке 1.

Рис. 1. Холст с размерами 800 х 480 пикселей легче увидеть в ландшафтном режиме.

Рис. 1. Холст с размерами 800 х 480 пикселей легче увидеть в ландшафтном режиме.

Помимо вялых игр, я обнаружил две проблемы при запуске SeaBattle в приложении браузера:

  • Отсутствие звука, возможно, из-за того, что файлы WAV не поддерживаются.
  • Браузер иногда застревает в цикле, где он постоянно отображает экран инициализации, за которым следует экран игрового процесса.

В качестве упражнения выясните причину первой проблемы и адаптируйте игру для компенсации. (Подсказка: вы можете идентифицировать текущий браузер с помощью navigator.userAgent.indexOf() и затем действовать соответствующим образом.) Однако, вторую проблему может оказаться сложнее решить.

Вывод

SeaBattle – это пример интересной игры, которую можно создать с помощью API-интерфейсов HTML5 Audio, Canvas и Web Storage. Теперь, когда у вас есть понимание того, как он взаимодействует с этими API-интерфейсами, вы можете улучшить игру. Вы можете начать с загрузки исходного кода SeaBattle . Если вы планируете монетизировать свою версию этой игры, не забудьте полностью протестировать игру на различных мобильных устройствах. Удачи!