Файл: Беляев С А - Разработка игр на языке JavaScript - 2016.pdf
Добавлен: 25.10.2018
Просмотров: 7589
Скачиваний: 136
Менеджер игры
101
Игра регулярно обновляется. Обновление игры может
происходить с частотой несколько раз в секунду. Чем чаще
оно происходит, тем более плавные движения на экране
наблюдает пользователь, тем большие требования предъ
являются к компьютеру, на котором выполняется про
грамма. Назовем это регулярное обновление «такт» игры.
Предположим ситуацию взаимного уничтожения двух
объектов, при последовательном выполнении команды
уничтожения будет уничтожен только один объект либо
придется реализовывать сложную логику по контролю
взаимного уничтожения. Есть простой подход — исполь
зовать объект отложенного удаления объектов, тогда оба
объекта поместят друг друга в массив laterKill и после
выполнения всех действий текущего такта достаточно
проверить и удалить хранящиеся в нем объекты.
initPlayer: function(obj) { this.player = obj; }
Функция initPlayer принимает в качестве параметра
объект (obj) и сохраняет его значение в поле player, инициа
лизирует игрока. Функция используется в parseEntities
менеджера карты (п. 2.3).
kill: function(obj) { this.laterKill.push(obj); }
Функция kill принимает в качестве параметра объект
(obj) и предназначена для сохранения его в массив laterKill
для отложенного удаления.
Действия, выполняемые программой в функциях об
новления игры на каждом такте (update), отображения
игрового поля пользователю (draw), загрузке данных игры
(loadAll) и запуске игры (play), могут существенно отли
чаться в зависимости от требований, предъявляемых к
игре. Рассмотрим возможный вариант реализации данных
функций.
function draw(ctx) {
for(var e = 0; e < this.entities.length; e++)
this.entities[e].draw(ctx);
}
Функция отображения игрового поля пользователю
(draw) принимает в качестве параметра контекст холста.
for(var e = 0; e < this.entities.length; e++)
102
Глава 6
Выполняется цикл по всем объектам карты, храня
щимся в массиве entities менеджера игры. В качестве ин
декса используется новая переменная e:
this.entities[e].draw(ctx);
Вызывается команда отображения (draw) для каждо
го объекта (entities[e]) карты.
function update() { // îáíîâëåíèå èíôîðìàöèè
if(this.player === null)
return;
// ïî óìîë÷àíèþ èãðîê íèêóäà íå äâèãàåòñÿ
this.player.move_x = 0;
this.player.move_y = 0;
// ïîéìàëè ñîáûòèå - îáðàáàòûâàåì
if (eventsManager.action["up"]) this.player.move_y = -1;
if (eventsManager.action["down"]) this.player.move_y = 1;
if (eventsManager.action["left"]) this.player.move_x = -1;
if (eventsManager.action["right"]) this.player.move_x = 1;
// ñòðåëÿåì
if (eventsManager.action["fire"]) this.player.fire();
// îáíîâëåíèå èíôîðìàöèè ïî âñåì îáúåêòàì íà êàðòå
this.entities.forEach(function(e) {
try { // çàùèòà îò îøèáîê ïðè âûïîëíåíèè update
e.update();
} catch(ex) {}
});
// óäàëåíèå âñåõ îáúåêòîâ, ïîïàâøèõ â laterKill
for(var i = 0; i < this.laterKill.length; i++) {
var idx = this.entities.indexOf(this.laterKill[i]);
if(idx > -1)
this.entities.splice(idx, 1); // óäàëåíèå èç ìàññèâà 1 îáúåêòà
};
if(this.laterKill.length > 0) // î÷èñòêà ìàññèâà laterKill
this.laterKill.length = 0;
mapManager.draw(ctx);
mapManager.centerAt(this.player.pos_x, this.player.pos_y);
this.draw(ctx);
}
Менеджер игры
103
Функция обновления (update) вызывается на каждом
такте игры и обеспечивает обновление информации об иг
роке и остальных объектах игры.
if(this.player === null) return;
Если игрок (player) не инициализирован, то функция
завершает свое выполнение. Следующие две строки ини
циализируют нулем параметры скорости игрока: this.
player.move_x = 0; this.player.move_y = 0. Данные строч
ки позволяют упростить программный код, используемый
для задания направления движения («up», «down», «left»,
«right»).
if (eventsManager.action["up"]) this.player.move_y = -1;
Проверяется условие, что необходимо выполнить дей
ствие «moveup», тогда объекту игрока устанавливается
направление движения вверх (скорость по вертикали —
отрицательная). Аналогичным образом настраиваются
направления движения вниз, влево и вправо.
if (eventsManager.action["fire"]) this.player.fire();
Проверяется условие, что необходимо выполнить дей
ствие «fire», тогда вызывается функция игрока fire.
this.entities.forEach(function(e) { try { e.update(); } catch(ex) {} });
Встроенная функция forEach массива принимает в ка
честве параметра функцию, которая должна быть вызва
на для каждого элемента массива. В качестве парамет
ра передается новая функция без имени с параметром e.
В теле функции использована конструкция try { … }
catch(ex) { … }, которая гарантирует, что в случае возник
новения ошибочной ситуации при обновлении объектов
массива не произойдет прекращение исполнения програм
мы. В теле try вызывается функция обновления (update)
для каждого элемента массива.
for(var i = 0; i < this.laterKill.length; i++)
После обновления всех объектов игры выполняется
цикл for по всем элементам, попавшим в массив отложен
ного удаления (laterKill).
var idx = this.entities.indexOf(this.laterKill[i]);
104
Глава 6
Встроенная функция массива indexOf определяет ин
декс в массиве элемента, laterKill[i], подлежащего удале
нию. Результат сохраняется в новую переменную idx.
if(idx > -1) this.entities.splice(idx, 1);
Выполняется проверка, что объект в массиве найти
удалось, после чего с использованием встроенной функ
ции массива splice выполняется удаление 1 элемента мас
сива, начиная с индекса idx. Данная функция позволяет
удалять произвольное количество элементов (не мень
ше 1).
if(this.laterKill.length > 0) this.laterKill.length = 0;
Если в массиве отложенного удаления есть элементы,
то все элементы удаляются путем установления длины
массива в ноль.
mapManager.draw(ctx);
Вызывается функция отображения карты (draw) ме
неджера карты.
mapManager.centerAt(this.player.pos_x, this.player.pos_y);
Вызывается функция изменения видимой области
(centerAt) менеджера карты в зависимости от позиции
игрока (player.pos_x, player.pos_y).
this.draw(ctx);
Выполняется отображение всех объектов, размещен
ных на карте.
Для корректной работы всех менеджеров игры они дол
жны быть корректно инициализированы в правильной
последовательности, для этого должна быть вызвана функ
ция загрузки (loadAll).
function loadAll() {
mapManager.loadMap("tilemap.json"); // çàãðóçêà êàðòû
spriteManager.loadAtlas("atlas.json", "img/tankattack.png");
// çàãðóçêà àòëàñà
gameManager.factory['Player'] = Player; // èíèöèàëèçàöèÿ ôàáðèêè
gameManager.factory['Tank'] = Tank;
gameManager.factory['Bonus'] = Bonus;
Менеджер игры
105
gameManager.factory['Rocket'] = Rocket;
mapManager.parseEntities(); // ðàçáîð ñóùíîñòåé êàðòû
mapManager.draw(ctx); // îòîáðàçèòü êàðòó
eventsManager.setup(canvas); // íàñòðîéêà ñîáûòèé
}
Использование отдельной функции загрузки позволя
ет вносить изменения только в одном методе при измене
нии исходных данных для программы.
mapManager.loadMap("tilemap.json");
Функция loadMap менеджера карты загружает карту
из файла «tilemap.json», при необходимости может исполь
зоваться относительный или абсолютный путь к файлу.
spriteManager.loadAtlas("atlas.json", "img/tankattack.png");
Функция loadAtlas менеджера спрайтов обеспечивает
загрузку атласа из файла «atlas.json» и изображения из
файла «img/tankattack.png». В данном случае для изоб
ражения использован относительный путь — изображе
ние находится во вложенной папке с именем «img».
Следующие четыре строки инициализируют фабрику
(factory) менеджера игры.
gameManager.factory['Player'] = Player;
Полю с именем «Player» присваивается указатель на
объект Player (разработан в п. 3.1). Аналогичные операции
выполняются для объектов Tank, Bonus и Rocket. В дан
ном случае имена полей — типы, которые использованы
при описании объектов в редакторе карт (п. 2.1).
mapManager.parseEntities();
После настройки фабрики вызывается функция parse
Entities редактора карты (п. 2.3).
mapManager.draw(ctx);
К данному моменту загружена карта, загружены все
объекты и спрайты, вызывается функции отображения
(draw) менеджера карты.
eventsManager.setup(canvas);