Пишем игру на HTML5 и JS. Часть 2.

No Comments

Продолжаем написание HTML5 игры. Теперь, когда готова модульная система, можем опробовать все её преимущества на деле. Это первое, что мы сделаем в этой части. А второе — это напишем модуль для работы с изображениями-спрайтами. Пока что только с неподвижными, анимация будет позже.

Переходим на модули.

Для начала, адаптируем имеющийся код.

minddef.html

В html файле после описания стилей уберём подключение старого js-скрипта. Затем включим загрузку js-классов, ядра системы и опишем модуль-запускальщик:

<script type="text/javascript" src="core/class.js"></script>
<script type="text/javascript" src="core/zx.js"></script>
<script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function() {
        zx.unit('main', {
            requires: ['minddef.game'],
            body: function() { MindDefGame.start('canvas', 320, 480); }
        });
    }, false);
 </script>

Теперь скрипт minddef/game.js не загружается явно. Вместо этого описан простой модуль main, которому для работы требуется модуль minddef.game. Тело main только лишь запускает новый метод start объекта MindDefGame, передавая id HTML5-элемента canvas, а также размеры, которые ему необходимо установить. Загрузчик модульной системы автоматически найдёт js-файл, в котором описан minddef.game (это как раз файл minddef/game.js, который теперь явно не подключается) и загрузит его перед выполнением модуля main. Если конечно придерживаться схемы именования файлов+модулей, описанной ранее ;)

Модуль minddef.game.

Здесь также изменения незначительны. Прежде всего определение переменной-объекта MindDefGame теперь находится в теле модуля:

zx.unit('minddef.game', {
    body: function() {
        MindDefGame = {
            //...

Прототип bind переехал в ядро: core/zx.js, а прежняя функция обработчика onload превратилась в метод start. Кроме того, метод initialize теперь сам ищет canvas по id и выставляет заданные размеры:

initialize: function(cnv, w, h) {
    var c = document.getElementById(cnv);
    if (c) {
        c.width = w;
        c.height = h;
        this.cnv = с;
        this.ctx = с.getContext('2d');
    }
    this.x = 0;
 },

//...

MindDefGame.start = function(cnv, w, h) {
    this.initialize(cnv, w, h);
    var fps = 60;
    this._interval = setInterval(this.loop.bind(MindDefGame), 1000/fps);
};

Модуль работы с изображениями.

Это один из основных модулей, поэтому описываем его первым. Изображения нам будут нужны для спрайтов, задних планов, шрифтов, текстур и может для чего-нибудь ещё. Хочется, чтоб мы могли с лёгкостью:

  • создавать изображения;
  • загружать их из файлов;
  • рисовать их на холсте в нужном месте, в нужном размере;
  • рисовать только фрагмент большого изображения также в любом месте холста + с возможностью масштабирования;
  • изображения подгружались бы автоматически при запуске загрузки ресурсов.

Опишем новый класс zx.Image, в котором будет реализован весь этот функционал. После этого можно будет легко создавать объекты, например так:

var droid = new zx.Image('img/android3.0.jpg');
this.mobilePicture = new zx.Image('pic/iphone.png');

Описание этого класса поместим внутри модуля core.image. Зависимостей от других модулей у него нет, так что получается довольно простой код:

zx.unit('core.image', {
    body: function () {
        zx.Image = Class.extend({   // Наследуем от Class, чтобы сработал конструктор
            data: null,   // Рисуемый объект: HTML объект Image (м.б. Canvas/Video)
            src: '',                // Аттрибут src - путь к файлу
            w: 0,                   // Ширина
            h: 0,                   // Высота
            loaded: false,          // Загружен ли файл
            init: function(src) {
                this.data	= new Image();
                this.src	= src;
                zx.addRes(this);    // Сообщаем ядру о новом ресурсе
            },

Загрузка изображений делается очень просто. Для этого в созданном объекте Image достаточно изменить свойство src. Кроме того, добавим обработчики окончания загрузки и ошибки. Свойство src устанавливаем в последнюю очередь, т.к. загрузка начинается сразу же после его изменения. И она может закончиться до того, как указан обработчик onload. Опишем метод load:

        load: function() {
            if (!this.loaded) {
                this.data.onload  = this.onLoad.bind(this);
                this.data.onerror = this.onError.bind(this);
                this.data.src     = this.src;
            }
        },

Для вывода изображений на «холст» (рисования) уже есть готовый метод drawImage, который позволяет делать практически всё, что нам надо и даже чуть больше. В качестве «источника» при рисовании можно использовать как объект Image (отображается на тэг <img>), так и canvas и даже video. В последнем случае будет отрисован текущий кадр. Опишем три метода, практически аналогичные методам drawImage контекста «холста»:

draw: function(dstX, dstY) {
    if (this.loaded) {
        zx.ctx.drawImage(this.data, dstX, dstY);
    }
},
drawScale: function(dstX, dstY, w, h) {
    if (this.loaded) {
        zx.ctx.drawImage(this.data, dstX, dstY, w || this.w, h || this.h);
    }
},
drawChunk: function(dstX, dstY, srcX, srcY, w, h, zoom) {
    if (this.loaded) {
        w = w || this.w;
        h = h || this.h;
        zoom = zoom || 1;
        zx.ctx.drawImage(this.data, srcX, srcY, w, h, dstX, dstY, w*zoom, h*zoom);
    }
},

Для удобства контекст canvas перемещаем в ядро в свойство zx.ctx. Это позволяет с лёгкостью добраться до него из любого экзэмпляра объекта zx.Image и из других классов в будущем.

Сейчас координаты, передаваемые этим методам — это абсолютные координаты на холсте. Позже, когда на холсте будет показана лишь часть карты, будет удобнее передавать глобальные (мировые) координаты объекта. Но пока подойдёт такой вариант.

Осталось  описать обработчики загрузки изображения, и наш модуль будет закончен:

            onLoad: function(e) {
                this.w      = this.data.width;
                this.h      = this.data.height;
                this.loaded = true;
            },
            onError: function(e) {
                throw("Can't load image: " + this.src);
            }
        });
    }
});

Почти всё готово, нереализованным остался последний пункт: «изображения подгружались бы автоматически при запуске загрузки ресурсов». В конструкторе нашего модуля есть заготовка для этого: zx.addRes(this); — это обращение к ядру. Ядру мы сообщаем, что необходимо добавить ресурс. И все ресурсы будут загружены перед стартом. Но об этом чуть позже, на сегодня всё.

Leave a Reply

You must be logged in to post a comment.