На дворе XXI век. 95% компьютерных пользователей активно пользуются интернетом и играют в браузерные игры. А это значит, что стоит всерьёз начать делать HTML5 игры.
История Crafty.js
Сегодняшний интернет держится на HTML5, а Crafty.js — это HTML5 открытый игровой движок, написанный на чистом JavaScript, предназначенный для создания 2D игр. Используя этот движок вы получите игру, работающую во всех основных браузеров, в том числе, на мобильных устойствах. Движок легко интегрируется с другими библиотеками: jQuery, Greensock. Одним из плюсов Crafty является то, что он может использовать Canvas и DOM для рендеринга графики, в том числе использовать их вместе. Это действительно очень круто; комбинируя такие механизмы отображения можно использовать движок для создания насыщенных веб-страниц даже для относительно старых браузеров.
Что будем делать
Игра будет называться Canvas Racer и являться наследником идей Zippy Race.
Ставлю цель показать правильное использование возможностей игрового движка Crafty, удаленный дебаг андроид-устройства через настольный Google Chrome, загрузка WOFF-шрифтов, работа с Canvas, DeviceOrientationEvent и другими модными HTML5 возможностями.
Устанавливаем движок
Установить движок можно несколькими способами.
1. Скачать скрипты движка из сайта http://craftyjs.com/.
2. Установить пакет через командную строку через NPM:
npm install craftyjs
3. Установить через Bower:
bower install crafty
Стартуем
Создаём страницу index.html и подключаем в тег head файл скрипта, в тег body добавляем контейнер для игры:
<div id="canvasRacer"></div>
Перед закрытием тега body создаём новый скрипт:
// инициализируем движок, передавая ему в аргументах ширину, высоту и id элемента игрового контейнера
Crafty.init(360, 640, 'canvasRacer')
// инициализируем canvas
Crafty.canvas.init()
Коллизии
Crafty поддерживает продвинутые системы распознавания ударов одного объекта с другим. За это взаимодействие отвечает компонент Collision. Для регистрации ударов необходимо явно определить полигоны. Если полигонов много я рекомендую использовать сервис http://os-class.ru/mapcreator/
Чтобы включить возможность дебага текущих коллизий добавьте компонент WiredHitBox и подсветите его, например, белым цветом:
Crafty
.e('PlayerCar')
.addComponent('WiredHitBox')
.debugStroke('white')
Собственные события
В движке есть множество встроенных событий для мыши и клавиатуры. Также можно легко создавать собственные события.
Например, для объекта obj это делается так:
obj.bind('myClick', function() {})
Вызываем
obj.trigger('myClick')
Компонентная модель
Crafty.js в отличие от многих других JavaScript-движков использует объектно-компонентную модель, что делает его необычайно гибким и более быстрым в использовании и проектировании. Компонент в 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
}
}
}
Вот так мы сделали геттер и сеттер имени игрока. Теперь к 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)
})
}
На финишной прямой!
Многим пришлось пожертвовать, что-то выпилить, что-то докрутить.
В итоге проект использует фичи:
- parse.com
- Bower
- FullScreen Video
- Gamepad API
- Device Orientation API
- Встраиваемый HTML на Crafty.js
- CSS3 WebFonts
- HTML5 Audio
- HTML5 GeoLocation
- Yandex Geocoding
- Crafty Debug
- CSS3 Media Query
Crafty замечательный игровой движок: он понятный, удобный, гибкий и мощный, к тому же он открытый и проверенный мною лично при создании HOG-игр. Помимо этого с ним можно создавать самые разные жанры. Жаль что такой крутой движок очень поздно пришел в мир WebGL и теперь ему очень тяжело угнаться за такими известными движками как Phaser или Cocos2d.
Исходный код игры доступен на гихабе:
https://github.com/qertis/CanvasRacer