От автора: Я собираюсь создать простую 2D игру на чистом JavaScript. Для первого шага я хочу показать, как анимировать (панорамировать или прокручивать) фоновое изображение, используя элемент canvas. Я также собираюсь показать основной код, необходимый для создания цикла, в котором мы сможем прорисовывать кадры.
Существуют два распространенных сценария для простых 2D игр:
Для всего уровня используется одно огромное изображение. Все действия происходят на одном и том же фоне, но меняется положение окна просмотра
Используется небольшое изображение, которое постоянно прокручивается (обычно справа налево) во время того как игрок продвигается
Панорамирование окна просмотра внутри фонового изображения
Демо
Нажмите на кнопку, чтобы запустить анимацию
Как это работает
У нас есть функция, которая вызывается для каждого кадра, который прорисовывает наша игра. Используя этот метод, мы вычисляем положение окна просмотра. В этом базовом примере я выбрал вычислять положение на основе прошедшего времени. Таким образом, камера двигается по эллиптическому пути.
1 2 3 4 5 6 7 |
function draw(delta) { totalSeconds += delta; var x = -1 * (img.width - canvas.width) / 2 * (1 + Math.cos(totalSeconds / Math.PI)); var y = -1 * (img.height - canvas.height) / 2 * (1 + -Math.sin(totalSeconds / Math.PI)); context.drawImage(img, x, y); } |
Бесконечная прокрутка фонового изображения
Во втором случае фоновое изображение прокручивается непрерывно по мере того как проходит время и продвигается игрок. Это похоже на игру Марио, только камера направлена на Марио все время.
На анимации выше мы можем видеть, что для этого эффекта нам понадобятся минимум два или более изображений (можно вставить одно изображение несколько раз), в зависимости от размера окна просмотра.
Демо
Нажмите на кнопку, чтобы запустить анимацию. Вертикальные черные линии обозначают границы каждого изображения. В этом примере я использую одно и то же изображение, при этом скорость прокрутки постоянная – 100 пикселей в секунду.
Как это работает
Положение фона также основывается на прошедшем времени (постоянная скорость)
Мы вычисляем количество изображений, необходимых для заполнения окна просмотра: Math.ceil(canvas.width / img.width) + 1
Мы вычисляем текущее положение по горизонтали: totalSeconds * vx % img.width. Обратите внимание на оператор %
Мы сохраняем текущее состояние контекста и переводим объект canvas, чтобы облегчить прорисовку
Мы прорисовываем изображения одно за другим
Мы восстанавливаем значение контекста
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function draw(delta) { totalSeconds += delta; var vx = 100; // the background scrolls with a speed of 100 pixels/sec var numImages = Math.ceil(canvas.width / img.width) + 1; var xpos = totalSeconds * vx % img.width; context.save(); context.translate(-xpos, 0); for (var i = 0; i < numImages; i++) { context.drawImage(img, i * img.width, 0); } context.restore(); } |
Весь код, вызываемый функцией draw()
Для того, чтобы это работало нам нужно произвести еще несколько действий. Вот базовый код, который я использовал в этих примерах.
Я использовал базовое полизаполнение requestAnimationFrame polyfill
Анимация начинается только если изображение успешно загружено (onload)
Логика запуска/остановки и кнопка
И функция цикла loop(), которая вызывается в каждом кадре во время анимации. Здесь нам нужна функция requestAnimationFrame().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
(function() { window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; var canvas = document.getElementById('bg'); var context = canvas.getContext('2d'); var looping = false; var totalSeconds = 0; var img = new Image(); img.onload = imageLoaded; img.src = 'IMG_SOURCE'; function imageLoaded() { draw(0); var btn = document.getElementById('btnStart'); btn.addEventListener('click', function() { startStop(); }); } var lastFrameTime = 0; function startStop() { looping = !looping; if (looping) { lastFrameTime = Date.now(); requestAnimationFrame(loop); } } function loop() { if (!looping) { return; } requestAnimationFrame(loop); var now = Date.now(); var deltaSeconds = (now - lastFrameTime) / 1000; lastFrameTime = now; draw(deltaSeconds); } function draw(delta) { /* Here happens some magic. */ } }()); |
Чтобы увидеть полный исходный код, перейдите на следующие страницы
Автор: Adam Beres-Deak
Источник: //bdadam.com/
Редакция: Команда webformyself.