Перейти к тексту

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Профиль создается, когда вы в первый раз заходите в developerWorks. Выберите данные в своем профиле (имя, страна/регион, компания) которые будут общедоступными и будут отображаться, когда вы публикуете какую-либо информацию. Вы можете изменить данные вашего ИБМ аккаунта в любое время.

Вся введенная информация защищена.

  • Закрыть [x]

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Вся введенная информация защищена.

  • Закрыть [x]

Разработка 2D-игр на HTML5: Графика и анимация

Рисование на холсте и приведение предметов в движение

Дэвид Гири, автор и докладчик, Clarity Training, Inc.
Дэвид Гири
Дэвид Гири (David Geary), автор книги Core HTML5 Canvas, является также сооснователем группы пользователей HTML5 Денвера и автором восьми книг по Java-программированию, в том числе бестселлеров по Swing и JavaServer Faces. Дэвид часто выступает на конференциях, в том числе JavaOne, Devoxx, Strange Loop, NDC и OSCON, и трижды заслужил титул "рок-звезды JavaOne". Для developerWorks он написал циклы статей JSF 2 fu и GWT fu. За новостями Дэвида можно следить в Twitter: @davidgeary.

Описание:  В предлагаемом цикле статей знаток HTML5 Дэвид Гири шаг за шагом демонстрирует процесс создания 2D-видеоигры на HTML5. Данная статья посвящена графике Canvas и анимации HTML5. Вы увидите, как изобразить графические элементы игры и как привести их в движение. Вы также познакомитесь с лучшим способом анимации на HTML5 и способами прокрутки фона и реализации параллакса для имитации трех измерений.

Больше статей из этой серии

Дата:  08.02.2013
Уровень сложности:  средний
Активность:  3507 просмотров
Комментарии:  


Графика и анимация - наиболее фундаментальные аспекты любой видеоигры, поэтому я начну эту статью с краткого обзора 2D API Canvas, а затем расскажу о реализации основной анимации в игре Snail Bait. Из этой статьи вы узнаете:

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

Конечный результат всего программирования, описанного в этой статье, показан на рисунке 1.


Рисунок 1. Прокрутка фона и контроль частоты кадров
Прокрутка Snail Bait

Горизонтальная прокрутка фона и платформ. Платформы находятся на переднем плане, поэтому они движутся заметно быстрее, чем фон, создавая мягкий эффект параллакса. В начале игры фон прокручивается справа налево. В конце уровня фон и платформы меняют направление движения.

Бегун на данном этапе разработки не движется. Кроме того, в игре еще нет обнаружения столкновений, поэтому когда под бегуном нет платформы, он повисает в воздухе.

Значки над холстом игры слева со временем будут указывать количество оставшихся жизней (как показано на рисунке 1 в первой статье цикла). Пока же в этом месте отображается текущая скорость анимации в кадрах в секунду.

Графика немедленного исполнения

Canvas представляет собой графическую систему немедленного исполнения, что означает, что она сразу отображает то, что указано, и немедленно забывает это. Другие графические системы, такие как масштабируемая векторная графика (SVG), реализуют режим с промежуточным хранением, что означает, что они ведут список отображаемых объектов. Не неся накладных расходов, связанных с ведением списка отображения, Canvas работает быстрее, чем SVG; однако если нужно учитывать объекты, которыми могут управлять пользователи, то в Canvas эту функциональность придется реализовать самостоятельно.

Прежде чем продолжить, попробуйте поиграть в игру, как она представлена на рисунке 1 ― вам будет легче понять ее код. (Реализация Snail Bait для этой статьи приведена в разделе Загрузки).

Обзор Canvas HTML5

2D-контекст Canvas представляет собой обширный графический API, который позволяет реализовать все от текстовых редакторов до видеоигр-платформеров. На момент написания этой статьи API содержит более 30 методов, но для Snail Bait используется всего горстка из них, показанная в таблице 1.


Таблица 1. Методы 2D-контекста Canvas, используемые в игре Snail Bait
МетодОписание
drawImage() Создает изображение или его часть в определенном месте холста. Можно также нарисовать другой холст или кадр из элемента video.
save() Сохраняет атрибуты контекста в стеке.
restore() Извлекает атрибуты контекста из стека и применяет их к контексту.
strokeRect() Рисует прямоугольный контур.
fillRect() Заполняет прямоугольник.
translate() Преобразование системы координат. Это мощный метод, который используется во многих сценариях. Вся прокрутка в Snail Bait осуществляется только путем вызова этого метода.

Маршрутная графика

Как и Apple Cocoa и Adobe Illustrator, API Canvas основан на маршрутах, то есть изображает графические примитивы на холсте, создавая маршрут, а затем прорисовывая или заполняя этот маршрут. Удобные методы strokeRect() и fillRect() соответственно вырисовывают или заполняют прямоугольник.

Все элементы Snail Bait, за исключением платформ, ― это растровые изображения. Фон, бегун и все предметы и монстры ― изображения, которые рисуются с помощью метода drawImage().

В конечном итоге Snail Bait будет применять spritesheet ― общую страницу, содержащую всю графику игры, ― но пока я буду использовать отдельные изображения фона и бегуна. Бегуна я рисую с помощью функции, показанной в листинге 1.


Листинг 1. Прорисовка бегуна
function drawRunner() {
   context.drawImage(runnerImage,                                        // изображение
                     STARTING_RUNNER_LEFT,                               // холст влево
                     calculatePlatformTop(runnerTrack) - RUNNER_HEIGHT); // холст вверх
}

Функция drawRunner() передает в drawImage() три аргумента: изображение и левую и верхнюю координаты для его размещения на холсте. Левая координата ― это константа, тогда как верхняя координата определяется платформой, на которой находится бегун.

Аналогичным образом нарисуем фон, как показано в листинге 2.


Листинг 2. Прорисовка фона
function drawBackground() {
   context.drawImage(background, 0, 0);
}

Универсальный метод drawImage()

С помощью метода drawImage() 2D-контекста Canvas можно нарисовать все изображение или любую прямоугольную область в пределах изображения в любом месте холста, при необходимости масштабируя это изображение. Кроме растровых изображений, с помощью метода drawImage() можно нарисовать содержимое другого холста или текущий кадр элемента video. Это всего один метод, но drawImage() облегчает реализацию интересных и иначе трудно реализуемых приложений, таких как программы видеоредактирования.

Функция drawBackground() из листинга 2 рисует фоновое изображение в точке холста с координатами (0,0). Позже мы изменим эту функцию для прокрутки фона.

Для рисования платформ, которые не являются растровыми изображениями, требуется более широкое использование API Canvas, как показано в листинге 3.


Листинг 3. Рисование платформ
var platformData = [
    // Экран 1.......................................................
    {
       left:      10,
       width:     230,
       height:    PLATFORM_HEIGHT,
       fillStyle: 'rgb(150,190,255)',
       opacity:   1.0,
       track:     1,
       pulsate:   false,
    },
    ...
 ],
 ...

function drawPlatforms() {
   var data, top;

   context.save(); // Сохранение текущего состояния контекста

   context.translate(-platformOffset, 0); // Преобразование системы координат 
                                             // для всех платформ

   for (var i=0; i < platformData.length; ++i) {
      data = platformData[i];
      top = calculatePlatformTop(data.track);

      context.lineWidth   = PLATFORM_STROKE_WIDTH;
      context.strokeStyle = PLATFORM_STROKE_STYLE;
      context.fillStyle   = data.fillStyle;
      context.globalAlpha = data.opacity;

      context.strokeRect(data.left, top, data.width, data.height);
      context.fillRect  (data.left, top, data.width, data.height);
   }

   context.restore(); // Восстановление сохраненного состояния контекста
}

Код JavaScript, приведенный в листинге 3, определяет массив с именем platformData. Каждый объект этого массива представляет собой метаданные, которые описывают отдельную платформу.

Функция drawPlatforms() использует методы strokeRect() и fillRect() контекста Canvas для рисования прямоугольников платформ. Характеристики этих прямоугольников — которые хранятся в объектах массива platformData — используются для задания стиля заливки контекста и атрибута globalAlpha, который задает степень непрозрачности всего, что впоследствии будет изображено на холсте.

Вызов context.translate() сдвигает систему координат холста — изображенную на рисунке 2 — на указанное количество пикселей в горизонтальном направлении. Это преобразование и установка атрибутов ― временные, потому что они выполняются между вызовами методов context.save() и context.restore().


Рисунок 2. Система координат Canvas по умолчанию
Система координат Canvas по умолчанию

По умолчанию начало системы координат находится в верхнем левом углу холста. Его можно перемещать с помощью метода context.translate().

Мы поговорим о прокрутке фона с помощью метода context.translate() в разделе Прокрутка фона. Но на данный момент вы знаете почти все, что нужно знать о Canvas HTML5, чтобы реализовать Snail Bait. В остальной части этого цикла статей я остановлюсь на других аспектах разработки игр HTML5, начиная с анимации.


Анимация HTML5

По сути, анимация реализуется просто: создается последовательность изображений, производящая впечатление, что объекты в некотором роде оживают. Это означает, что необходимо реализовать цикл, в котором повторяется прорисовка изображения.

Традиционно анимационные циклы реализуются на JavaScript с помощью метода setTimeout() или, как показано в листинге 4, setInterval().


Листинг 4. Реализация анимации с помощью метода setInterval()

setInterval( function (e) { // Не делайте этого для анимаций, где важно время
   animate();               // Функция, рисующая текущий кадр анимации
}, 1000 / 60);              // Приблизительно 60 кадров в секунду (fps)
  

Практический совет

Никогда не используйте функции setTimeout() или setInterval() для анимаций, в которых важно время.

Код из листинга 4, несомненно, произведет анимацию, многократно вызывая функцию animate(), изображающую следующий кадр; однако результаты могут вас не устроить, потому что методы setInterval() и setTimeout() ничего не знают об анимации. (Примечание. Вам придется реализовать функцию animate(); она не входит в API Canvas.)

В листинге 4 я установил интервал 1000/60 мс, что соответствует примерно 60 кадрам в секунду. Это моя эмпирическая оценка оптимальной частоты анимации, и она может оказаться не очень хорошей; однако так как методы setInterval() и setTimeout() ничего не знают об анимации, им приходится указывать частоту кадров. Было бы лучше, если бы частоту кадров выбирал браузер, который, несомненно, лучше меня знает, когда рисовать следующий кадр анимации.

Существует еще более серьезный недостаток использования методов setTimeout и setInterval(). Мы передаем им временные интервалы в миллисекундах, но эти методы не обеспечивают точность до миллисекунды; на самом деле, согласно спецификации HTML, они ― в целях экономии ресурсов ― могут значительно превысить заданный интервал.

Во избежание этих проблем не следует использовать методы setTimeout() и setInterval() для анимаций, где важно время; используйте метод requestAnimationFrame().

Метод requestAnimationFrame()

В спецификации Управление временем в анимациях на основе сценариев (см. раздел Ресурсы) W3C определяет метод работы с объектом window, называемый requestAnimationFrame(). В отличие от setTimeout() или setInterval(), метод requestAnimationFrame() специально предназначен для анимации. Он не страдает недостатками, присущими методам setTimeout() и setInterval(). К тому же он прост в применении, как показывает листинг 5.


Листинг 5. Реализация анимации с помощью метода requestAnimationFrame()


function animate(time) {           // Цикл анимации
   draw(time);                     // Функция, рисующая текущий кадр анимации
   requestAnimationFrame(animate); // Продолжение анимации
};

requestAnimationFrame(animate);    // Запуск анимации  

Методу requestAnimationFrame() передается ссылка на функцию обратного вызова, и когда браузер готов нарисовать следующий кадр анимации, он выполняет этот обратный вызов. Для продолжения анимации также выполняется обратный вызов requestAnimationFrame().

Как видно из листинга 5, браузер передает в функцию обратного вызова параметр time. Вы можете спросить, что именно означает параметр time? Это текущее время? Время, за которое браузер должен нарисовать следующий кадр анимации?

Как ни странно, но никакого определения этого времени не существует. Единственное, что можно с уверенностью сказать, это то, что для каждого браузера этот параметр всегда означает одно и то же; его можно использовать для вычисления времени между кадрами, как я покажу в разделе Расчет частоты анимации в кадрах/с.

Полифилл requestAnimationFrame()

Во многих отношениях HTML5 – это мечта программиста. Разработчики могут использовать HTML5, свободный от проприетарных API, для реализации приложений, работающих на разных платформах в вездесущих браузерах. Спецификация быстро прогрессирует, постоянно впитывая новые технологии и совершенствуя существующую функциональность.

Полифиллы: программирование будущего

В прошлом большинство кроссплатформенных программ создавалось под "наименьший общий знаменатель" браузеров. Полифиллы меняют это положение, предоставляя доступ к передовым функции браузера, если они поддерживаются, а при необходимости возвращаясь к менее современным реализациям.

Однако новая технология часто прокладывает путь в спецификацию через существующую функциональность браузеров. Поставщики снабжают такую функциональность префиксом, чтобы не путать ее с реализациями в других браузерах; например, метод requestAnimationFrame() первоначально был реализован в Mozilla как mozRequestAnimationFrame(). Затем он появился в WebKit, где эта функция называется webkitRequestAnimationFrame(). Наконец, W3C стандартизировала его как requestAnimationFrame().

Реализации с префиксами поставщиков и разная поддержка стандартных реализаций усложняет использование новой функциональности, поэтому сообщество HTML5 придумало то, что называетя polyfill. Полифилл определяет уровень поддержки браузером определенной функции и либо предоставляет прямой доступ к ней, если она реализована в браузере, либо заменяет временной реализацией, наиболее близкой к стандартной функциональности.

Полифиллы просты в применении, но могут быть сложными для реализации. В листинге 6 показана реализация полифилла для функции requestAnimationFrame().


Листинг 6. Полифилл requestNextAnimationFrame()


// Из книги Core HTML5 Canvas

window.requestNextAnimationFrame =
   (function () {
      var originalWebkitRequestAnimationFrame = undefined,
          wrapper = undefined,
          callback = undefined,
          geckoVersion = 0,
          userAgent = navigator.userAgent,
          index = 0,
          self = this;

      // Обходной путь для бага Chrome 10, из-за которого Chrome не передает время 
      // в функцию анимации
      
      if (window.webkitRequestAnimationFrame) {
         // Определение оболочки

         wrapper = function (time) {
           if (time === undefined) {
              time = +new Date();
           }
           self.callback(time);
         };

         // Переход
          
         originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;    

         window.webkitRequestAnimationFrame = function (callback, element) {
            self.callback = callback;

            // Браузер вызывает оболочку, а та делает обратный вызов
            
            originalWebkitRequestAnimationFrame(wrapper, element);
         }
      }

      // Обходной путь для Gecko 2.0, где баг в mozRequestAnimationFrame() ограничивает 
      // анимацию 30-40 кадрами/с.

      if (window.mozRequestAnimationFrame) {
         // Проверка версии Gecko. Gecko используется не-Firefox браузерами. Gecko 2.0 
         // соответствует Firefox 4.0.
         
         index = userAgent.indexOf('rv:');

         if (userAgent.indexOf('Gecko') != -1) {
            geckoVersion = userAgent.substr(index + 3, 3);

            if (geckoVersion === '2.0') {
               // Заставляет оператор return выйти из цикла через функцию setTimeout().

               window.mozRequestAnimationFrame = undefined;
            }
         }
      }
      
      return window.requestAnimationFrame   ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame    ||
         window.oRequestAnimationFrame      ||
         window.msRequestAnimationFrame     ||

         function (callback, element) {
            var start,
                finish;


            window.setTimeout( function () {
               start = +new Date();
               callback(start);
               finish = +new Date();

               self.timeout = 1000 / 60 - (finish - start);

            }, self.timeout);
         };
      }
   )
();

Полифилл: определение

слово polyfill является гибридом из слов polymorphism (полиморфизм) и backfill (заполнять). Как и в случае полиморфизма, полифиллы выбирают соответствующий код во время выполнения ― и дополняют его недостающей функциональностью.

Полифилл, реализованный в листинге 6, добавляет функцию с именем requestNextAnimationFrame() к объекту window. Включение Next в имя функции позволяет отличать ее от исходной функции requestAnimationFrame().

Функция, которую полифилл присваивает requestNextAnimationFrame(), это либо requestAnimationFrame(), если браузер поддерживает ее, либо реализация с префиксом поставщика. Если браузер не поддерживает ни того, ни другого, функция предлагает специальную реализацию с использованием setTimeout() для наилучшей возможной имитации метода requestAnimationFrame().

Почти вся сложность полифилла связана с обходом двух багов и составлением кода перед оператором return. Первый баг относится к Chrome 10, который передает неопределенное значение времени. Второй баг, относящийся к Firefox 4.0, ограничивает частоту кадров до 35-40 кадров в секунду.

Реализация полифилла requestNextAnimationFrame() интересна, но ее не обязательно понимать; достаточно знать, как его использовать, что я и проиллюстрирую в следующем разделе.


Игровой цикл

Теперь, когда мы разобрались с графикой и анимацией, пришло время привести Snail Bait в движение. Для начала включим код JavaScript requestNextAnimationFrame() в HTML-код игры, как показано в листинге 7.


Листинг 7. HTML
<html>
   ...

   <body>
      ...

      <script src='js/requestNextAnimationFrame.js'></script>
      <script src='game.js'></script>
   </body>
</html>

В листинге 8 показан цикл анимации игры, который обычно называют игровым циклом.


Листинг 8. Игровой цикл
var fps;

function animate(now) { 
   fps = calculateFps(now); 
   draw();
   requestNextAnimationFrame(animate);
} 

function startGame() {
   requestNextAnimationFrame(animate);
}

Функция startGame(), которая вызывается обработчиком событий onload фонового изображения, начинает игру, вызывая полифилл requestNextAnimationFrame(). Когда нужно нарисовать первый кадр анимации игры, браузер вызывает функцию animate().

Функция animate() вычисляет частоту кадров анимации, учитывая текущее время. (Подробнее о значении time см. в разделе Метод requestAnimationFrame().) Вычислив частоту кадров, animate() вызывает функцию draw(), которая рисует следующий кадр анимации. Далее animate() вызывает метод requestNextAnimationFrame() для продолжения анимации.

Вычисление скорости анимации в кадрах/с

В листинге 9 показано, как Snail Bait вычисляет частоту кадров и обновляет индикатор частоты кадров, изображенный на рисунке 1.


Листинг 9. Расчет частоты кадров и обновление элемента fps

var lastAnimationFrameTime = 0,
    lastFpsUpdateTime = 0,
    fpsElement = document.getElementById('fps');

function calculateFps(now) {
 var fps = 1000 / (now - lastAnimationFrameTime);
 lastAnimationFrameTime = now;

 if (now - lastFpsUpdateTime > 1000) {
 lastFpsUpdateTime = now;  fpsElement.innerHTML = fps.toFixed(0) + ' fps';
 }
 return fps;
}  

Частота кадров ― это просто время, прошедшее с момента отображения предыдущего кадра анимации, поэтому правильнее было бы назвать это интервалом анимации. Можно принять более строгий подход и поддерживать среднюю частоту кадров в течение нескольких кадров, но я не вижу необходимости в этом; время, прошедшее с момента демонстрации последнего кадра анимации, ― это именно то, что нам понадобится в разделе Движение на основе времени.

Листинг 9 иллюстрирует также важный прием анимации: решение задачи со скоростью, отличной от скорости анимации. Если обновлять индикатор кадров в секунду на каждом кадре анимации, то он будет нечитаемым, потому что всегда будет находиться в движении; вместо этого я обновляю индикатор один раз в секунду.

Имея игровой цикл и частоту кадров, можно приступать к прокрутке фона.


Прокрутка фона

Фон Snail Bait, показанный на рисунке 3, медленно прокручивается в горизонтальном направлении.


Рисунок 3. Фоновое изображение
Фоновое изображение snail bait

Фон прокручивается плавно, потому что левый и правый края фона идентичны, как показано на рисунке 4.


Рисунок 4. Идентичные края обеспечивают гладкие переходы (слева: правый край; справа: левый край)
Идентичные края фоновых изображений

Snail Bait бесконечно прокручивает фон, прорисовывая его дважды, как показано на рисунке 5. Сначала, как показано в верхней части рисунка 5, фоновое изображение слева находится на экране целиком, тогда как справа оно полностью за кадром. Со временем фон прокручивается, что иллюстрируется на среднем и нижнем скриншотах на рисунке 5.


Рисунок 5. Прокрутка справа налево: полупрозрачные области соответствуют частям изображения, находящимся за пределами экрана
Прокрутка фона Snail Bait

В листинге 10 приведен код, соответствующий рисунку 5. Функция drawBackground() рисует изображение дважды, всегда на одних и тех же местах. Кажущаяся прокрутка является результатом постоянного преобразования системы координат холста влево, что создает впечатление прокрутки фона вправо.

(Вот как можно примирить кажущееся противоречие между преобразованием влево, но прокруткой вправо: представьте себе, что холст ― это пустая рамка поверх длинного листа бумаги. Бумага ― это система координат, а ее преобразование влево соответствует движению влево под рамкой [холстом]. В результате кажется, что холст перемещается вправо.)


Листинг 10. Прокрутка фона
var backgroundOffset; // Устанавливается перед вызовом drawBackground()

function drawBackground() {
   context.translate(-backgroundOffset, 0);

   // Первоначально на экране:
   context.drawImage(background, 0, 0);

   // Первоначально вне экрана:
   context.drawImage(background, background.width, 0);

   context.translate(backgroundOffset, 0);
}

Функция setBackground() смещает контекст холста на -backgroundOffset пикселей в горизонтальном направлении. Если значение backgroundOffset положительно, фон прокручивается вправо; если отрицательно, ― влево.

После перевода фона drawBackground() дважды рисует фоновое изображение, а затем возвращает контекст туда, где он был перед вызовом drawBackground().

Остается одно, казалось бы, тривиальное вычисление: расчет значения backgroundOffset, которое определяет, насколько сдвигать систему координат холста для каждого кадра анимации. Хотя сам расчет действительно тривиален, он имеет большое значение, поэтому я остановлюсь на этом подробнее.


Движение на основе времени

Частота кадров анимации будет меняться, но нельзя допустить, чтобы это влияло на скорость движения. Например, Snail Bait прокручивает фон со скоростью 42 пиксела/с независимо от базовой частоты кадров анимации. Анимация должна быть основана на времени ― то есть значения скоростей должны указываться в пикселях в секунду ― и не должна зависеть от частоты кадров.

При использовании движения, основанного на времени, легко вычислить количество пикселей, на которые объект должен перемещаться за один кадр: разделив скорость движения на текущую частоту кадров. Скорость (в пикселях в секунду), деленная на частоту кадров (в кадрах в секунду), дает число пикселей на кадр, то есть количество пикселей, на которое необходимо переместить что-то для текущего кадра.

Практический совет

Скорость анимации не должна зависеть от частоты кадров.

В листинге 11 показано, как Snail Bait использует движение, основанное на времени, для вычисления смещения фона:


Листинг 11. Задание скорости смещения фона
var BACKGROUND_VELOCITY = 42, // пикселя/с
    bgVelocity = BACKGROUND_VELOCITY;

function setBackgroundOffset() {
   var offset = backgroundOffset + bgVelocity/fps; // Движение на основе времени

   if (offset > 0 && offset < background.width) {
      backgroundOffset = offset;
   }
   else {
      backgroundOffset = 0;
   }
}

Функция setBackgroundOffset() вычисляет количество пикселей, на которое перемещается фон для текущего кадра, путем деления скорости движения фона на текущую частоту кадров. Затем это значение прибавляется к текущему смещению фона.

Для непрерывной прокрутки фона setBackgroundOffset() сбрасывает смещение фона в 0, когда оно становится меньше 0 или больше ширины фона.


Параллакс

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

Игра Snail Bait ― это 2D-платформер, но в ней используется мягкий эффект параллакса ― платформы кажутся ближе к наблюдателю, чем фон. В игре параллакс реализуется путем заметно более быстрой прокрутки платформ по сравнению с фоном.

Рисунок 6 иллюстрирует, как Snail Bait реализует параллакс. Верхний скриншот показывает фон в определенный момент времени, а нижний ― спустя несколько кадров анимации. На этих двух скриншотах видно, что за одно и то же время платформы продвинулись гораздо дальше, чем фон.


Рисунок 6. Параллакс: платформы (вблизи) прокручиваются быстрее, чем фон (вдали)
Параллакс

На листинге 12 показаны функции, которые регулируют скорость и смещение платформ.


Листинг 12. Настройка скорости и смещения платформ
var PLATFORM_VELOCITY_MULTIPLIER = 4.35; 

function setPlatformVelocity() {
   // Платформы движутся в 4,35 раза быстрее, чем фон
   platformVelocity = bgVelocity * PLATFORM_VELOCITY_MULTIPLIER; 
}

function setPlatformOffset() {
   platformOffset += platformVelocity/fps; // Движение на основе времени
}

Вспомните листинг 8 с игровым циклом Snail Bait. Этот цикл состоит из функции animate(), вызываемой браузером, когда нужно нарисовать следующий кадр анимации игры. Эта функция animate(), в свою очередь, вызывает функцию draw(), которая рисует следующий кадр анимации. В листинге 13 показан код функции draw() на данном этапе разработки.


Листинг 13. Функция draw()


function setOffsets() {
  setBackgroundOffset();
  setPlatformOffset();
}
function draw() {
  setPlatformVelocity();
  setOffsets();

  drawBackground();

  drawRunner();
  drawPlatforms();
}  

Функция draw() задает скорость платформ и смещение фона и платформ. Затем она рисует фон, бегуна и платформы.


В следующей статье

В следующей статье я покажу, как инкапсулировать код Snail Bait в объект JavaScript во избежание конфликтов пространств имен. Я также покажу, как приостановить игру, в том числе как автоматически приостанавливать ее, когда окно теряет фокус, и как перезапустить игру с отсчетом времени, когда окно восстанавливает фокус. Вы также увидите, как управлять бегуном с клавиатуры. По ходу дела вы научитесь использовать в игровом цикле CSS-переходы и функциональность interject. До скорой встречи.



Загрузка

ОписаниеИмяРазмерМетод загрузки
Пример кодаj-html5-game2.zip737 КБHTTP

Информация о методах загрузки


Ресурсы

Научиться

Получить продукты и технологии

  • Replica Island: загрузите открытый исходный код этого популярного платформера для Android.

Обсудить

  • Примите участие в деятельности сообщества developerWorks. Общайтесь с другими пользователями developerWorks, просматривая блоги, форумы, группы и вики разработчиков

Об авторе

Дэвид Гири

Дэвид Гири (David Geary), автор книги Core HTML5 Canvas, является также сооснователем группы пользователей HTML5 Денвера и автором восьми книг по Java-программированию, в том числе бестселлеров по Swing и JavaServer Faces. Дэвид часто выступает на конференциях, в том числе JavaOne, Devoxx, Strange Loop, NDC и OSCON, и трижды заслужил титул "рок-звезды JavaOne". Для developerWorks он написал циклы статей JSF 2 fu и GWT fu. За новостями Дэвида можно следить в Twitter: @davidgeary.

Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Спасибо. Эта запись была помечена для модератора.


Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Сообщение о нарушении не было отправлено. Попробуйте, пожалуйста, позже.


developerWorks: вход


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Выберите ваше отображаемое имя

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

(Должно содержать от 3 до 31 символа.)


Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Оценить эту статью

Комментарии

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source, Web-архитектура
ArticleID=857623
ArticleTitle=Разработка 2D-игр на HTML5: Графика и анимация
publish-date=02082013