Файл: Беляев С А - Разработка игр на языке JavaScript - 2016.pdf
Добавлен: 25.10.2018
Просмотров: 7597
Скачиваний: 136
36
Глава 2
Встроенная функция setTimeout принимает два пара
метра: первый — функция, которая будет вызвана после
заданной задержки, второй — задержка в миллисекундах.
В данном примере через 100 мс будет повторно вызвана
функция draw с тем же параметром.
if(this.tLayer === null)
При создании объекта tLayer присвоено значение null,
при первом обращении к draw данное условие будет верно.
for (var id = 0; id < this.mapData.layers.length; id++)
Цикл по массиву слоев в mapData.
var layer = this.mapData.layers[id];
Для сокращения записи создаем дополнительную пе
ременную layer.
if (layer.type === "tilelayer")
Если тип layer соответствует слою блоков карты, то
сохраняем его в tLayer и с использованием ключевого сло
ва break прерываем выполнение цикла.
for (var i = 0; i < this.tLayer.data.length; i++)
Цикл for по всем данным, предназначенным для ото
бражения на карте.
Если this.tLayer.data[i] равен нулю, то ничего делать
не нужно.
var tile = this.getTile(this.tLayer.data[i]);
С помощью функции getTile, которая будет описана
ниже, по номеру блока получаем из массива tilesets объект
блока и сохраняем его в переменной tile.
Следующие две строки позволяют вычислить pX и
pY — координаты блока в пикселах. Символ «%» обозна
чает вычисление остатка от деления двух чисел. Индекс i
проходит последовательно по всем элементам массива data
(в п. 2.1 был описан принцип хранения информации в этом
массиве). Значение pX вычисляется как остаток от деле
ния индекса i на количество элементов в строке (xCount),
для перевода в пикселы выполняется умножение на ши
Отображение карты игры
37
рину в пикселах (tSize.x). Значение pY вычисляется как
наименьшее целое (Math.floor) от деления i на количество
элементов в строке (xCount), для перевода в пикселы вы
полняется умножение на высоту в пикселах (tSize.y).
ctx.drawImage(tile.img, tile.px, tile.py, this.tSize.x, this.tSize.y, pX, pY,
this.tSize.x, this.tSize.y);
Для контекста вызывается функция drawImage с рас
ширенным количеством параметров: tile.img — изображе
ние, tile.px и tile.py — координаты блока в изображении,
this.tSize.x и this.tSize.y — ширина и высота блока в изоб
ражении, pX и pY — координаты, где необходимо отобра
зить блок, this.tSize.x и this.tSize.y — размеры отобража
емого блока. Размеры отображаемого блока необходимо
указывать, так как данная функция поддерживает изме
нение масштаба.
Для корректной работы функции draw должна быть
определена функция getTile, обеспечивающая получение
блока по ее индексу из tilesets.
function getTile (tileIndex) { // èíäåêñ áëîêà
var tile = { // îäèí áëîê
img: null, // èçîáðàæåíèå tileset
px: 0, py: 0 // êîîðäèíàòû áëîêà â tileset
};
var tileset = this.getTileset(tileIndex);
tile.img = tileset.image; // èçîáðàæåíèå èñêîìîãî tileset
var id = tileIndex - tileset.firstgid; // èíäåêñ áëîêà â tileset
// áëîê ïðÿìîóãîëüíûé, îñòàòîê îò äåëåíèÿ íà xCount äàåò x
// â tileset
var x = id % tileset.xCount;
// îêðóãëåíèå îò äåëåíèÿ íà xCount äàåò y â tileset
var y = Math.floor(id / tileset.xCount);
// ñ ó÷åòîì ðàçìåðà ìîæíî ïîñ÷èòàòü êîîðäèíàòû áëîêà
// â ïèêñåëàõ
tile.px = x * mapManager.tSize.x;
tile.py = y * mapManager.tSize.y;
return tile; // âîçâðàùàåì áëîê äëÿ îòîáðàæåíèÿ
}
Для отображения блока необходимы изображение, его
координаты в пикселах и размеры. Размеры блоков хра
38
Глава 2
нятся в mapManager, поэтому достаточно создать объект
(tile), который будет хранить три поля: изображение (img)
и координаты блока в изображении в пикселах (px, py).
var tileset = this.getTileset(tileIndex);
Для получения tileset по индексу (tileIndex) восполь
зуемся функцией getTileset, которая будет описана ниже
в данной главе. Код данной функции не сложен, его мож
но было бы разместить непосредственно в getTile, но он
потребуется для повторного использования, поэтому це
лесообразно его иметь в виде отдельной функции.
tile.img = tileset.image;
Выполняет копирование ссылки на изображение в но
вый объект tile.
var id = tileIndex - tileset.firstgid;
Переменная tileIndex хранит номер блока в общем мас
сиве data, при этом tileset.firstgid хранит номер первого
блока в отображаемом изображении. Вычитание выполня
ется для получения индекса блока в отображаемом tileset.
Для получения координат x и y выполняются уже из
вестные операции по вычислению на основании индекса
блока в изображении. Для получения координат (tile.px,
tile.py) выполняется умножение на размеры блока.
return tile;
В результате возвращается сформированный блок (tile).
Для корректной работы функции getTile необходима
функция getTileset.
function getTileset(tileIndex) { // ïîëó÷åíèå áëîêà ïî èíäåêñó
for (var i = mapManager.tilesets.length - 1; i >= 0; i--)
// â êàæäîì tilesets[i].firstgid çàïèñàíî ÷èñëî,
// ñ êîòîðîãî íà÷èíàåòñÿ íóìåðàöèÿ áëîêîâ
if (mapManager.tilesets[i].firstgid <= tileIndex) {
// åñëè èíäåêñ ïåðâîãî áëîêà ìåíüøå ëèáî ðàâåí èñêîìîìó,
// çíà÷èò ýòîò tileset è íóæåí
return mapManager.tilesets[i];
}
return null; // Âîçâðàùàåòñÿ íàéäåííûé tileset
}
Отображение карты игры
39
Создается цикл поиска:
for (var i = mapManager.tilesets.length - 1; i >= 0; i--)
В отличие от предыдущих описанных циклов for в дан
ном цикле поиск осуществляется не по возрастанию ин
декса, а по его убыванию (i означает уменьшение значе
ния переменной i на каждом шаге цикла). Поиск нужен
именно в обратном порядке, так как в tilesets[i].firstgid
хранится индекс, с которого начинается нумерация бло
ков, а сами наборы блоков упорядочены по возрастанию
этого индекса. Соответственно, если искомый индекс мень
ше максимального индекса в текущем наборе блоков, то
он в одном из предыдущих наборов блоков.
if (mapManager.tilesets[i].firstgid <= tileIndex)
Если искомый индекс больше начального номера бло
ков в tilesets[i], то именно этот набор блоков нужен, он
возвращается return tileset.
return null;
По окончании, если ничего не найдено, возвращается
null.
Приведенного кода достаточно для создания карты,
которая помещается на холсте. В случае, если карта на
холсте не помещается, необходимо внести дополнитель
ные изменения в код. В частности, потребуется дополни
тельное поле в mapManager, которое будет хранить пара
метры видимой области карты:
var mapManager = {
,
// âèäèìàÿ îáëàñòü ñ êîîðäèíàòàìè ëåâîãî âåðõíåãî óãëà
view: {x: 0, y: 0, w: 800, h: 600}
}
Поле view хранит координаты левого верхнего угла
видимой области (x, y) и размеры холста (w, h), ширину и
высоту. Недостаточно знать только размеры видимой об
ласти, полезно принимать во внимание эту информацию
при отображении, в частности сдвигать отображаемые объ
екты с учетом координат левого верхнего угла и учитывать
размеры холста при отображении. Нет смысла выводить
40
Глава 2
информацию за пределами холста, пользователь все рав
но этого не увидит, а ресурсы компьютера на это будут
расходоваться.
В функцию draw перед вызовом ctx.crawImage необ
ходимо добавить несколько строк кода:
// íå ðèñóåì çà ïðåäåëàìè âèäèìîé çîíû
if(!this.isVisible(pX, pY, this.tSize.x, this.tSize.y))
continue;
// ñäâèãàåì âèäèìóþ çîíó
pX -= this.view.x;
pY -= this.view.y;
В первой строке вызывается функция isVisible. Она
описана в виде функции, так как потребуется для повтор
ного использования, ее код будет приведен ниже в данной
главе. Функция isVisible принимает в качестве парамет
ров координаты (pX, pY) и размеры отображаемого блока
(this.tSize.x, this.tSize.y).
Если isVisible возвращает false, то используется клю
чевое слово continue для перехода к следующему шагу
цикла. В отличие от ключевого слова break, которое пре
рывает выполнение цикла, continue сообщает, что следу
ет проигнорировать весь код до конца цикла, изменить
счетчик цикла и начать выполнение цикла с начала.
Последующие две строчки уменьшают pX и pY с уче
том координат левого верхнего угла. Знак «=» предлагает
уменьшить значение переменной, стоящей слева от него,
на значение выражения, стоящего справа от него. Соот
ветственно, они могут быть переписаны таким образом:
pX = pX - this.view.x;
pY = pY - this.view.y;
Обе записи идентичны. Для корректной работы усо
вершенствованной функции draw необходима isVisible.
function isVisible(x, y, width, height) { // íå ðèñóåì çà ïðåäåëàìè âèäèìîé
// çîíû
if (x + width < this.view.x || y + height < this.view.y ||
x > this.view.x + this.view.w || y > this.view.y + this.view.h)
return false;
return true;
}