Thursday, October 27, 2011

Простая игра на HTML5 Canvas - часть 1


Предисловие от автора перевода.

Итак, я наконец-то подготовил перевод статьи Михала Будзинского открывающей нам новый web-стандарт HTML5. Этот стандарт еще не имеет утвержденной спецификации и представлен на сайте w3.org только как рабочий черновик, но уже активно продвигается производителями браузеров. Новшества, вносимые новой версией HTML (язык разметки гипертекстов) сближают его с языками программирования. Благодаря активному развитию социальных сетей, «облачных» сервисов и т.п. все яснее прослеживается необходимость улучшения интеграции мультимедийного контента и новый тэг <canvas> стал одним из решений данного вопроса представленный консорциумом.
Движок игры написан на JavaScript и те, кто еще не знаком с этим языком программирования найдут здесь также замечательные примеры для его начального освоения.

Введение.

StH - очень простой клон Doodle Jump, но чтобы быть до конца честным, я был вдохновлен Icy Tower. Ну да ладно.
Цель игры состоит в том, чтобы управлять маленьким ангелом и скакать на двух видах платформ - оранжевые (обычные) и зеленые (трамплины). Игра заканчивается, когда ангел падает за нижний край экрана.
Я создал эту игру за 8 часов и позже, когда довольно долго играл, я обнаружил немного ошибок,  так что в этом уроке, я хочу исправить их все. Давайте приступим!

Фон.

Поскольку вся игра, включая изображения и сценарии (скрипты), не должна быть больше 10КБ, я не захотел использовать растровое изображение на фоне. Гораздо экономичнее рисовать используя функции рисования <canvas>.
Прежде всего, мы нуждаемся в небольшом HTML, ничего особенного, только один <canvas> элемент с некоторым уникальным id, немного CSS и несуществующий, пока еще, game.js:
<html>
  <head>
    <title> Простая игра с HTML5 Canvas </ title>
  <style>
  body {margin:0px; padding:0px; text-align: center}
  canvas {outline:0; border:1px solid # 000; margin: 0 auto}
  </ style>
  </ head>
  <body>
    <canvas id='c'></ canvas>
    <script src="game.js"></ script>
  </ body>
</ html>
Это и есть весь HTML, который нам понадобится в течение этого урока. Хорошо, теперь давайте создавать Javascript. Прежде всего мы должны создать немного глобальных (хотя, я знаю что это Глобальное зло =) переменных и изменить атрибуты (свойства) <canvas>. Этого будет достаточно:
var width = 320, //ширина canvas
    height = 500, //высота canvas

  c = document.getElementById('c'), //сам canvas

  ctx = c.getContext('2d');
//двумерный контектс canvas (сейчас только он поддерживается всеми
//браузерами)

c.width = width;
c.height = height;
//устанавливаем размеры canvas

Первое, что важно понять про <canvas>, это то, что невозможно просто перемещать объекты на его поверхности. Нужно, обязательно, на каждом кадре его очищать, целиком или частично. Поэтому давайте создадим функцию очистки – clear():
var clear = function(){
  ctx.fillStyle = '#d0e7f9';
//выбираем цвет заливки (чудесный голубой)
//Использование clearRect() вызывало ошибку, в 2-х строках ниже показан
//старый вариант
//ctx.clearRect(0, 0, width, height);
//очистка экрана
  ctx.beginPath();
//запускаем рисование
  ctx.rect(0, 0, width, height);
//рисуем прямоугольник из точки (0, 0) до (widthheight) заполняя весь наш
//холст <canvas>
  ctx.closePath();
//заканчиваем рисование
  ctx.fill();
//заполняем прямоугольник цветом выбранным ранее
}

Одноцветный фон скучен как ад, так что давайте нарисуем несколько облаков на нем. Скорее не правильные облака, а простые, полупрозрачные круги, подражающие облакам. Круги мы будем рисовать в случайных местах холста, каждый с различным размером и прозрачностью. Мы будем держать всю информацию о кругах в 2-ом массиве (в JS их нет, но лучший путь к решению этой проблемы помещение одного массива в другой).

var howManyCircles = 10, circles = [];

for (var i = 0; i < howManyCircles; i++)
  circles.push([Math.random() * width, Math.random() * height, Math.random() * 100, Math.random() / 2]);
//добавляем информацию о кругах
//в массив 'circles'. Это x и y позиция,
//радиус в диапазоне 0-100 и прозрачность
//в диапазоне 0-0.5 (0 это абсолютно прозрачно, 1 непрозрачно)

var DrawCircles = function(){
  for (var i = 0; i < howManyCircles; i++) {
    ctx.fillStyle = 'rgba(255, 255, 255, ' + circles[i][3] + ')';
//белый цвет с прозрачностью в rgba
    ctx.beginPath();
    ctx.arc(circles[i][0], circles[i][1], circles[i][2], 0, Math.PI * 2, true);
//arc(x, y, radius, startAngle, endAngle, anticlockwise)
    ctx.closePath();
    ctx.fill();
  }
};

Хорошо, но скучновато. Почему облака стоят на месте? Давайте сделаем маленькую функцию с одним аргументом, которая сдвигает облака вниз на заданное число пикселей, и, когда определенный круг скрывается за пределами холста, он перемещается  наверх с изменением координаты X, радиуса и прозрачности:
var MoveCircles = function(deltaY){
  for (var i = 0; i < howManyCircles; i++) {
    if (circles[i][1] - circles[i][2] > height) {
//круг достигший нижнего края
//меняет параметры
      circles[i][0] = Math.random() * width;
      circles[i][2] = Math.random() * 100;
      circles[i][1] = 0 - circles[i][2];
      circles[i][3] = Math.random() / 2;
    } else {
//сдвигаем круг на deltaY пикселей вниз
      circles[i][1] += deltaY;
    }
  }
};

Теперь, не в последнюю очередь, давайте создадим основной цикл игры и подключим в нем все, что уже создали. Каждый кадр будет очищать экран, перемещать круги на 5px вниз, рисовать их и после 1/50 секунды вызывать следующий кадр. Я использую два setTimeouts вместо одного setInterval, хотя незнаю почему:). Вроде бы раньше были некоторые проблемы с производительностью в IE, что ли. И не забудьте добавить gLoopв глобальные переменныеобъявленные вначале.
var width = 320, 
//ширина холста <canvas>
  height = 500, 
//высота холста <canvas>
  gLoop,
(...) //остальная часть кода находится здесь

var GameLoop = function(){
  clear();
  MoveCircles(5);
  DrawCircles();
  gLoop = setTimeout(GameLoop, 1000 / 50);
}
GameLoop();

Благодарю Луиса Гирибоуна за комментарии к уроку. Он помог разобраться в чем отличие setTimeOut от setInterval. SetInterval запускает функцию переданную ему в аргументе не дожидаясь окончания предыдущей итерации, а setTimeOut ждет ее завершения даже если вышло время переданное в качестве второго аргумента.
Так же хочу поблагодарить пользователя под ником Ped7g за выявленные ошибки.
Результат этой части урока можно посмотреть по адресу: http://jsbin.com/odoho3, а скачать исходники тут: http://github.com/michalbe/Simple-game-with-HTML5-Canvas

Копирайты.

Автор: Михал Будзинский https://twitter.com/#!/@michalbe
Автор перевода: Андрей Семенов https://twitter.com/#!/a_semenov79

1 comment:

  1. HDR фильтр на canvas http://siteacademy.ru/index.php/htmldev/38-html/html5-image-effects-html-canvas

    ReplyDelete