Если вы хотите писать казуальные игры, используя элемент HTML5 Canvas, вам нужно найти способ обработки ваших спрайтов. Есть несколько библиотек, которые помогут вам писать игры, такие как ImpactJS , CraftyJS и так далее. Со своей стороны, я решил использовать EaselJS, который использовался для написания PiratesLoveDaisies : HTML5 Tower Defense. В этом уроке мы увидим, как использовать существующие элементы спрайта и анимировать их .
Вы знаете, что я делаю это на языке мольеров, а также на французском языке: Jeux HTML5: анимация, дизайн, холст и легкость
Эта статья является первой из серии из 3:
— HTML5 Gaming: анимация спрайтов в Canvas с EaselJS
— HTML5 Gaming: создание основных объектов и обработка коллизий с EaselJS
— HTML5 Platformer: полный порт игры XNA для <canvas> с EaselJS
Вступление
На официальном сайте EaselJS вы найдете несколько интересных примеров и базовую документацию . Мы будем использовать образец спрайтов в качестве базы. Мы также будем использовать ресурсы, доступные в образце XNA 4.0 Platformer . Для тех из вас, кто читает мой блог, вы, возможно, помните, что я люблю играть с этим образцом. Вот предыдущие прикрепленные статьи:
— Начальный комплект платформера Windows Phone 7 для XNA Studio 4.0
— Редактор уровня платформера XNA Silverlight 4 для Windows Phone 7
Тем временем образец платформера был обновлен нашей командой XNA и доступен здесь для Xbox 360, ПК и Windows Phone 7: App Hub — платформер . Вы можете скачать его, чтобы поиграть с ним и извлечь спрайты, чтобы использовать их с EaselJS.
В этой статье мы будем использовать эти 2 PNG-файла в качестве источника последовательности спрайтов:
Наш монстр бежит:
который содержит 10 разных спрайтов.
Наш монстр в режиме ожидания:
который содержит 11 различных спрайтов.
Примечание: эти примеры не работают должным образом в Firefox 5.0 из- за ошибки в их реализации canvas . Он был хорошо протестирован в IE9 / IE10 / Chrome 12 / Opera 11 и Firefox Aurora 7.0.
Урок 1: создание SpriteSheet и BitmapSequence
Мы начнем с перемещения бегущего монстра между началом и концом ширины нашего холста.
Первый шаг — загрузить полную последовательность, содержащуюся в файле PNG, с помощью этого кода:
var imgMonsterARun = new Image(); function init() { //find canvas and load images, wait for last image to load canvas = document.getElementById("testCanvas"); imgMonsterARun.onload = handleImageLoad; imgMonsterARun.onerror = handleImageError; imgMonsterARun.src = "img/MonsterARun.png"; }
Этот код будет вызываться первым для инициализации содержимого нашей игры. После загрузки мы можем начать игру. EaselJS предоставляет объект SpriteSheet для обработки спрайта. Таким образом, используя этот код:
var spriteSheet = new SpriteSheet( imgMonsterARun, //image to use 64, //width of each sprite 64, //height of each sprite { walk_left: [0, 9] });
Мы указываем, что мы хотели бы создать новую последовательность с именем walk_left, которая будет состоять из изображения imgMonsterARun . Это изображение будет разбито на 10 кадров размером 64х64 пикселя. Это основной объект для загрузки нашего спрайта и создания наших последовательностей. При желании может быть создано несколько последовательностей из одного и того же файла PNG, например, в примере спрайтов крыс сайта EaselJS.
После этого нам нужно использовать объект BitmapSequence . Это помогает нам анимировать нашу последовательность и позиционировать наши спрайты на экране. Давайте рассмотрим код инициализации этой BitmapSequence:
// create a BitmapSequence instance to display and play back the sprite sheet: bmpSeq = new BitmapSequence(spriteSheet); // set the registration point (the point it will be positioned and rotated around) // to the center of the frame dimensions: bmpSeq.regX = bmpSeq.spriteSheet.frameWidth/2|0; bmpSeq.regY = bmpSeq.spriteSheet.frameHeight / 2 | 0; // start playing the first sequence: bmpSeq.gotoAndPlay("walk_left"); //animate // set up a shadow. Note that shadows are ridiculously expensive. You could display hundreds // of animated rats if you disabled the shadow. bmpSeq.shadow = new Shadow("#454", 0, 5, 4); bmpSeq.name = "monster1"; bmpSeq.speed = 1; bmpSeq.direction = 90; bmpSeq.vX = 3|0.5; bmpSeq.vY = 0; bmpSeq.x = 16; bmpSeq.y = 32; // have each monster start at a specific frame bmpSeq.currentFrame = 0; stage.addChild(bmpSeq);
Конструктор BitmapSequence объекта просто нуждается в SpriteSheet элемент в качестве параметра. Затем мы даем имя последовательности, устанавливая некоторые параметры, такие как скорость и начальная позиция нашего первого кадра. Наконец, мы добавляем эту последовательность в список отображения с помощью объекта Stage и его метода addChild () .
Следующий шаг — решить, что бы мы хотели сделать в нашем цикле анимации. Этот цикл анимации вызывается каждые xxx миллисекунды и позволяет вам обновлять положение ваших спрайтов. Для этого EaselJS предоставляет объект Ticker, который обеспечивает централизованную передачу тика или пульса с заданным интервалом. Все, что вам нужно сделать, это подписаться на событие tick и реализовать метод .tick (), который будет вызываться обратно. Этот код, например, регистрирует событие:
Ticker.addListener(window); // Best Framerate targeted (60 FPS) Ticker.setInterval(17);
А вот код, который будет вызываться каждые 17 мс (когда это возможно), чтобы обновить положение нашего монстра:
function tick() { // Hit testing the screen width, otherwise our sprite would disappear if (bmpSeq.x >= screen_width - 16) { // We've reached the right side of our screen // We need to walk left now to go back to our initial position bmpSeq.direction = -90; } if (bmpSeq.x < 16) { // We've reached the left side of our screen // We need to walk right now bmpSeq.direction = 90; } // Moving the sprite based on the direction & the speed if (bmpSeq.direction == 90) { bmpSeq.x += bmpSeq.vX; bmpSeq.y += bmpSeq.vY; } else { bmpSeq.x -= bmpSeq.vX; bmpSeq.y -= bmpSeq.vY; } // update the stage: stage.update(); }
Вы можете проверить окончательный результат здесь :
Вы также можете просмотреть этот пример здесь: easelJSSpritesTutorial01, если вы хотите просмотреть полный исходный код.
Но ждать! В этой анимации есть 2 проблемы:
1 — шаги анимации странные, так как персонаж, кажется, слишком быстро
проходит по последовательности различных спрайтов 2 — персонаж может нормально ходить только справа налево, в противном случае похоже, что он пытается достичь своего рода лунного танца.
Давайте посмотрим, как это исправить во втором уроке.
Урок 2: управление скоростью анимации и переключение спрайтов
Чтобы зафиксировать скорость анимации, я нашел самый простой способ — использовать оператор модуля, чтобы избежать рисования / обновления моей последовательности во время каждого тика. В настоящее время существует открытая проблема по этой теме: https://github.com/gskinner/EaselJS/issues/60 с EaselJS 0.3.2.
Чтобы добавить новые кадры анимации, чтобы персонаж мог нормально ходить слева направо, нам нужно перевернуть каждый кадр. EaselJS предоставляет объект SpriteSheetUtils для этого и метод flip () . Вот код, который их использует:
// {nameOfFlippedSequence:["derivativeSequence", flipHorizontally, flipVertically, optionNameOfNextSequence]} spriteSheet = SpriteSheetUtils.flip( spriteSheet, { walk_right: ["walk_left", true, false, null] });
Мы создаем производную последовательность с именем « walk_right » на основе последовательности «walk_left» , что мы переворачивать горизонтально. Наконец, вот код, который замедляет скорость анимации и обрабатывает последовательность воспроизведения на основе позиции персонажа:
function tick() { // To slow down the animation loop of the sprite, we're not redrawing during each tick // With a Modulo 4, we're dividing the speed by 4 var speedControl = Ticker.getTicks() % 4; if (speedControl == 0) { // Hit testing the screen width, otherwise our sprite would disappear if (bmpSeq.x >= screen_width - 16) { // We've reached the right side of our screen // We need to walk left now to go back to our initial position bmpSeq.direction = -90; bmpSeq.gotoAndPlay("walk_left") } if (bmpSeq.x < 16) { // We've reached the left side of our screen // We need to walk right now bmpSeq.direction = 90; bmpSeq.gotoAndPlay("walk_right"); } // Moving the sprite based on the direction & the speed if (bmpSeq.direction == 90) { bmpSeq.x += bmpSeq.vX; bmpSeq.y += bmpSeq.vY; } else { bmpSeq.x -= bmpSeq.vX; bmpSeq.y -= bmpSeq.vY; } // update the stage: stage.update(); } }
Вы можете проверить окончательный результат здесь :
Вы также можете просмотреть этот пример здесь: easelJSSpritesTutorial02, если вы хотите просмотреть полный исходный код.
Урок 3: загрузка нескольких спрайтов и игра с несколькими анимациями
Пришло время загрузить состояние простоя монстра. Идея здесь состоит в том, чтобы воспроизвести состояние анимации для достижения единственного кругового обхода. Когда мы достигнем этого, мы будем играть в состоянии ожидания
Затем мы будем загружать несколько файлов PNG с веб-сервера. Тогда очень важно дождаться загрузки всех ресурсов, в противном случае вы можете попытаться нарисовать еще не загруженные ресурсы. Вот очень простой способ сделать это:
var numberOfImagesLoaded = 0; var imgMonsterARun = new Image(); var imgMonsterAIdle = new Image(); function init() { //find canvas and load images, wait for last image to load canvas = document.getElementById("testCanvas"); imgMonsterARun.onload = handleImageLoad; imgMonsterARun.onerror = handleImageError; imgMonsterARun.src = "img/MonsterARun.png"; imgMonsterAIdle.onload = handleImageLoad; imgMonsterAIdle.onerror = handleImageError; imgMonsterAIdle.src = "img/MonsterAIdle.png"; } function handleImageLoad(e) { numberOfImagesLoaded++; // We're not starting the game until all images are loaded // Otherwise, you may start to draw without the resource and raise // this DOM Exception: INVALID_STATE_ERR (11) on the drawImage method if (numberOfImagesLoaded == 2) { numberOfImagesLoaded = 0; startGame(); } }
Этот код очень прост. Например, он не обрабатывает ошибки должным образом, пытаясь повторно загрузить изображение в случае первого сбоя. Если вы создаете игру, вам нужно написать собственный менеджер загрузки контента, если используемая вами библиотека JS не реализует его.
Чтобы добавить последовательность простоя и установить параметры положения, нам просто нужно использовать тот же код, который мы видели ранее:
var spriteSheetIdle = new SpriteSheet( imgMonsterAIdle, //image to use 64, //width of each sprite 64, //height of each sprite { idle: [0, 10] }); bmpSeqIdle = new BitmapSequence(spriteSheetIdle); bmpSeqIdle.name = "monsteridle1"; bmpSeqIdle.speed = 1; bmpSeqIdle.direction = 0; bmpSeqIdle.x = 16; bmpSeqIdle.y = 32;
Теперь в методе tick () нам нужно остановить анимацию ходьбы, как только мы дойдем до левой стороны экрана, и вместо этого воспроизвести анимацию простоя. Вот код, который делает это:
if (bmpSeq.x < 16) { // We've reached the left side of our screen // We need to walk right now bmpSeq.direction = 90; bmpSeq.gotoAndStop("walk_left"); stage.removeChild(bmpSeq); bmpSeqIdle.gotoAndPlay("idle"); stage.addChild(bmpSeqIdle); }
Вы можете проверить окончательный результат здесь :
Вы также можете просмотреть этот образец здесь: easelJSSpritesTutorial03, если вы хотите просмотреть полный исходный код.
Это все, ребята! Но если вы хотите пойти дальше, вы можете прочитать следующую часть здесь: HTML5 Gaming: создание основных объектов и обработка коллизий с EaselJS
Дэвид