Быстрый старт с Crafty

На дворе XXI век, 95% компьютерных пользователей активно пользуются интернетом и играют в браузерные игры.

Немного про Crafty.js

Сегодняшний интернет держится на HTML5, а Crafty.js - это HTML5 открытый и гровой движок, написанный на чистом JavaScript, предназначенный для создания 2D игр. Используя этот движок вы получите игру работающую во всех основных браузеров, в том числе, на мобильных устойствах. Движок также не конфликтует с другими библиотеками: jQuery, Greensock и др.
Одним из плюсов Crafty является то, что он может использовать Canvas и DOM рендеринг графики, в том числе вместе. И это действительно очень круто. Комбинируя такие механизмы отображения, можно использовать движок для создания насыщенных веб-страниц даже для относительно старых браузеров.

Устанавливаем движок

Установить движок можно несколькими способами.
Самый очевидный - зайти на сайт http://craftyjs.com/ и скачать скрипты движка оттуда.

Или установить пакет через командную строку через NPM 
npm install craftyjs

Или установить через Bower 
bower install crafty

Стартуем

Создаём страницу index.html и подключаем в тег head файл скрипта, в тег body добавляем контейнер для игры:

Перед закрытием тега body создаём новый скрипт:

// инициализируем движок, передавая ему в аргументах ширину, высоту и id элемента игрового контейнера
Crafty.init(360, 640, 'canvasRacer');
// инициализируем canvas
Crafty.canvas.init();

Коллизии

Crafty поддерживает продвинутые системы распознавания ударов одного объекта с другим. За это взаимодействие отвечает компонент Collision. Для регистрации ударов необходимо явно определить полигоны. Если полигонов очень много я рекомендую использовать сервис
http://os-class.ru/mapcreator/index.htm

Собственные события

В движке есть множество встроенных событий для мыши, клавиатуры. Также можно легко создавать собственные события.
Например, для объекта obj это делается так:
obj.bind('myClick', function() {})
Вызываем
obj.trigger('myClick')

Компонентная модель

Crafty.js в отличие от многих других JavaScript-движков использует объектно-компонентную модель, что делает его необычайно гибким и более быстрым в использовании и проектировании. Этим он похож на Unity3d.
Компонент в Crafty представляет собой объект, который может очень просто наследовать другие компоненты. Компоненты могут отвечать за что-угодно: устройство ввода, персонажа, игровую сетку или даже отдельную анимацию.

Новый компонент в Crafty создается таким способом:
Crafty.c('ComponentName', {})

Crafty поддерживает карты спрайтов, а значит можно очень просто делать анимации, записав все анимации персонажа в одном графическом файле. Стоит заранее заметить что огромные png-куски не скармливаются некоторыми браузерами. Так что без фоторедактора тут не обойтись. Как гласит поговорка: "Лучше меньше, да лучше".

Поиск по компонентам можно делать так:
Crafty('ComponentName') или даже так:
Crafty('ComponentName').each(function(obj) {})

Давайте создадим компонент Car который будем использовать в PlayerCar.
Crafty.c('Car', {
  _speed: 0.0, // это приватный атрибут
  //В момент создания компонента, запускается функция init, если она существует
  init: function () {
    this
      .requires('2D, Canvas, Tween, Collision, Sprite') //компонуем разные компоненты
      .origin('top center') // изменяем позицию вращения относительно спрайта
      .attr({ _speed: 0.0 }) // еще один способ создания атрибута
  }
});

Хорошие практики

Для JavaScript рекомендуется не создавать новые свойства в глобальном объекте. Crafty загружается, но позволяет добавлять новые объекты в свой объект прототипа. Делается это через Crafty.extend({ player: {} })

Создадим класс player со свойством name
Crafty.extend({
  player: {
    _name: '',
    getName: function() {
      return this._name;
    },
    setName: function(playerName) {
      this._name = playerName;
    }
  }
}

Вот так мы обернули в Crafty запись и извлечение имени игрока. Теперь к player теперь можно обратиться через Crafty.player

Изменяемый HTML

В Crafty.js рекомендуется делать объекты цепочечными, для этого передаем в конце функции компонента return this.
Управляемый HTML код доступен из коробки: Например обычная кнопка делается так:

Crafty.c('Button', {
  _button: null, 
  init: function () {
    this.requires('HTML, Mouse');
    var button = document.createElement('button');
    button.style.color = '#333';
    button.style.backgroundColor = 'ghostwhite';
    button.style.padding = '4px 12px';
    button.style.border = 'none';
    this._button = button;
  },
  setSize: function (width, height) {
    this._button.style.width = width || this.w + 'px';
    this._button.style.height = height || this.h + 'px';
    //делаем замещение HTML
    this.replace(this._button.outerHTML);
    return this;
  },
  setText: function (text) {
    this._button.textContent = text;
    this.replace(this._button.outerHTML);
    return this;
  }
});

Инициализация кнопки:
Crafty.e('Button')  .setText('my Button')  .setSize(100, 100) 

Текстовые шрифты

Кроме спрайтовых шрифтов очень часто требуется использование обычных текстовых шрифтов.

Создадим компонент для шрифта:
Crafty.c('DefaultFont', {
  init: function () { 
    this
      .requires("2D, Canvas, Text")
      .text('')
      .origin("center")
      .textColor('#FF0000')
      .textFont({
        size: '20px'
      });
  }
});

Мы можем наследовать обычный шрифт для генерации жирного шрифта, а также явно использовать familyFont. Сторонний шрифт предварительно надо установить через CSS Web Fonts.

Crafty.c('BoldFont', { 
  init: function () {
    this.requires("DefaultFont") 
      .textFont({
        size: '28px',
        type: 'normal',
        weight: 'bold',
        family: 'Lobster'
      });
  }
});

Работаем с изображениями

Для добавления простой картинки на игровом холсте нужно добавить компонент Image, установить ширину и высоту, а также явно выбрать картинку через функцию .image. Эта функция имеет два параметра: 1 - путь к ресурсу картинки, 2 - стиль отображения повторения.

this 
  .requires('2D, Canvas, Image')
  .attr({
    x: 0,
    y: 0,
    z: 0,
    w: 100, //ширина
    h: 100 //высота
  })
  .image("content/images/road_texture.jpg", "repeat-y") 

Сцены

Сцены декларируются через метод defineScene.

Crafty.defineScene('level', levelInit, levelOut);
function levelInit() {
  Crafty.background('white');
  Crafty.e('Track');
}
function levelOut() {
  Crafty('Delay').each(function () {
  this.destroy();
}); 
Для перемещения между сценами используем метод scene:
Crafty.scene('sceneName')

Загрузка ресурсов

Хорошей практикой для начала будет загружать ресурсы перед самой первой сценой. Это могут быть картинки, аудио, шрифты и пр.

Crafty.paths({ 
  images: "content/images/",
  audio: "content/audio/"
});

var assetsObj = {
  "audio": {
  "crash": ["crash/crash.wav"],
  "horn1": ["horn/horn1.mp3"],
  "music": ["music/music.ogg"]
}, 

"images": [ 
  "game_over.png",
  "menu.jpg",
  "controls/dpad.png",
  "road_texture.jpg",
  "speedometer/arrow.png",
  "speedometer/speedometer.png"
], 

"sprites": {
  "controls/buttons.png": { 
  "tile": 48,
  "tileh": 48,
  "map": {
    "pause": [0, 0],
    "soundOn": [1, 0],
    "soundOff": [2, 0],
    "play": [3, 0],
    "share": [4, 0],
    "fullscreen": [5, 0]
  }
},
"vehicles.png": {
  "tile": 128,
  "tileh": 284,
  "map": {
    playerCar: [0, 0], car1: [1, 0], car2: [2, 0], car3: [3, 0], car4: [4, 0]
  }
},
"tires.png": {
  "tile": 19,
  "tileh": 44,
  "map": {
    playerTire: [0, 0]
  }
},
"logo.png": { 
  "tile": 208,
  "tileh": 212,
  "map": {
    "logo": [0, 0]
  }
}; 
// вызываем загрузку ресурсов
function loadAssets() { 
  Crafty.load(assetsObj, function () {
      Crafty('LoadingText').text('Loading complete');
      Crafty.scene('menu');
    }, function (e) {
      Crafty('LoadingIndicator').w = Crafty.viewport.width / (100 / e.percent);
      Crafty('LoadingText').text('Loading: ' + '(' + e.loaded + '/' + e.total + ')');
    }, function (e) { //uh oh, error loading
      console.error(e);
  });
} 

Дебажим

Чтобы включить возможность дебага текущих коллизий добавьте компонент WiredHitBox и подсветите его, например, белым цветом:

Crafty
  .e('PlayerCar')
  .addComponent('WiredHitBox')
  .debugStroke('white')

Обратите внимание на появившуюся вокруг авто белую коллизийную рамку

Вывод

Финальная версия
Crafty замечательный игровой движок: он понятный, удобный, гибкий и мощный, к тому же он открытый и проверенный мною лично при создании HOG-игр. Помимо этого с ним можно создавать самые разные жанры. Жаль что такой крутой движок очень поздно пришел в мир WebGL и теперь ему очень тяжело угнаться за такими известными движками как Phaser или Cocos2d.

Исходный код игры доступен на гихабе:
https://github.com/qertis/CanvasRacer


Советую к ознакомлению: Создаем HTML5 игру