HTML5 мобильные игры

Думаете о создании кроссплатформенных играх с помощью HTML5? Без использования Java или Objective-C? В обход магазинов мобильных приложений? Это кажется невероятным!

На сегодняшний день небольшая горстка разработчиков мобильных игр продвигает группу HTML5 мобильных игр. Nutmeg__ и Lunch Bug, яркие примеры подобных игр для мобильных устройств. Самое привлекательное в этих играх то, что запускаются они как с мобильного устройства, так и со стационарного компьютера, используя один и тот же код. Может быть, HTML5, наконец, выполнит священный завет «написать один раз, запускай везде».

Начало

Перед тем, как вы начнете разрабатывать очередной Temple Run или Angry Birds, вам следует учесть несколько вещей, которые помогут вам быстрее достигнуть поставленной цели:

  • Эффективность. Мобильные браузеры традиционно работают не с помощью пылающих двигателей JavaScript.  С IOS 6 и Chrome Beta для Android, ситуация значительно улучшается.
  • Разрешающая способность. Множество устройств на Android предлагает поистине изобилие решений. Такие устройства, как iPhone 4 и IPad 3 известны своим повышенным разрешением и плотностью пикселей.
  • Звук. Надеюсь, вам понравится звук тишины. Поддержка аудио в мобильных браузерах оставляет желать лучшего, если не сказать больше. Большинство устройств предлагают только один канал. IOS не будет даже грузить звук, пока пользователь не инициирует это действие. Мой совет, ждите новых разработчиков мобильных браузеров, которые найдут решение.

Сейчас, как разработчик приложений, вы привыкли иметь дело с причудами некоторых браузеров и фрагментированными платформами. Так что технические проблемы вас не пугают, не так ли? Более того, все эти проблемы, в том числе и с аудио, носят временный характер. Мобильные браузеры меняются так быстро, что в скором времени эти проблемы просто перестанут существовать.

В этом уроке мы будем делать относительно простую игру, что поможет вам понять основы и избежать ошибок. Результат будет выглядеть так:

Это довольно простая игра, в которой пользователь должен разрывать плавающие пузыри, прежде чем они достигнут верхней части экрана. Я назвал наше маленькое усилие Pop.

Мы будем создавать игру в несколько этапов:

  1. Обеспечение поле индикации и оптимизации для мобильных устройств;
  2. Краткий обзор использования API;
  3. Ввод вариантов прикосновения;
  4. Создание основного цикла игры;
  5. Представление спрайтов;
  6. Добавление эффектов, чтобы оживить игру;
  7. Добавление немного блеска и некоторых базовых эффектов.

Установка сцены

Достаточно вступления. Запустите ваш любимый текстовый редактор, сварите крепкий кофе, и давайте начнем.

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

Давайте начнем с HTML:

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">

<meta name="viewport" content="width=device-width,
    user-scalable=no, initial-scale=1, maximum-scale=1, user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

<style type="text/css">
body { margin: 0; padding: 0; background: #000;}
canvas { display: block; margin: 0 auto; background: #fff; }
</style>

</head>

<body>

<canvas> </canvas>
<script>
// all the code goes here
</script>

</body>
</html>

Metaтеги говорят мобильным браузерам, отключить масштабирование и сформировать полный размер, а не уменьшать страницу. Следующий apple- префикс мета-тег позволяют игре, становится закладкой. На iPhone, закладка программы не отображается на панели инструментов в нижней части страницы, тем самым освобождая ценное пространство.

Смотрим дальше:

// namespace our game
var POP = {

    // set up some initial values
    WIDTH: 320,
    HEIGHT:  480,
    // we'll set the rest of these
    // in the init function
    RATIO:  null,
    currentWidth:  null,
    currentHeight:  null,
    canvas: null,
    ctx:  null,

    init: function() {

        // the proportion of width to height
        POP.RATIO = POP.WIDTH / POP.HEIGHT;
        // these will change when the screen is resized
        POP.currentWidth = POP.WIDTH;
        POP.currentHeight = POP.HEIGHT;
        // this is our canvas element
        POP.canvas = document.getElementsByTagName('canvas')[0];
        // setting this is important
        // otherwise the browser will
        // default to 320 x 200
        POP.canvas.width = POP.WIDTH;
        POP.canvas.height = POP.HEIGHT;
        // the canvas context enables us to
        // interact with the canvas api
        POP.ctx = POP.canvas.getContext('2d');

        // we're ready to resize
        POP.resize();

    },

    resize: function() {

        POP.currentHeight = window.innerHeight;
        // resize the width in proportion
        // to the new height
        POP.currentWidth = POP.currentHeight * POP.RATIO;

        // this will create some extra space on the
        // page, allowing us to scroll past
        // the address bar, thus hiding it.
        if (POP.android || POP.ios) {
            document.body.style.height = (window.innerHeight + 50) + 'px';
        }

        // set the new canvas style width and height
        // note: our canvas is still 320 x 480, but
        // we're essentially scaling it with CSS
        POP.canvas.style.width = POP.currentWidth + 'px';
        POP.canvas.style.height = POP.currentHeight + 'px';

        // we use a timeout here because some mobile
        // browsers don't fire if there is not
        // a short delay
        window.setTimeout(function() {
                window.scrollTo(0,1);
        }, 1);
    }

};

window.addEventListener('load', POP.init, false);
window.addEventListener('resize', POP.resize, false);

Во-первых, мы создаем POP имя для нашей игры. Будучи хорошими разработчиками, мы не хотим загрязнять глобальное пространство названий. В соответствии с хорошей практикой, мы будем объявлять все переменные в начале программы. Большинство из них очевидны: холст canvas заменяется на холст canvas элемент в HTML, и ctx позволяет получить к нему доступ через JavaScript API.

В POP.init, мы получаем ссылку на наш элемент, получаем его контент и корректируем размеры до 480 × 320. Функции изменения замеров, которая изменяет размеры и нагрузку событий, регулируется на атрибуте canvas’ style, ширина и высота соответственно при сохранении соотношения. По сути, canvas имеет все те же размеры, но был уменьшен с использованием CSS. Попробуйте изменить размер вашего браузера, и вы увидите, результат нашего преобразования.

Если вы пробовали это на вашем телефоне, вы заметите, что адресная строка остается видимой. Тьфу! Мы можем исправить это, добавив несколько дополнительных пикселей в документе, а затем, прокрутив вниз, чтобы скрыть адресную строку, например, так:

// we need to sniff out Android and iOS
// so that we can hide the address bar in
// our resize function
POP.ua = navigator.userAgent.toLowerCase();
POP.android = POP.ua.indexOf('android') > -1 ? true : false;
POP.ios = ( POP.ua.indexOf('iphone') > -1 || POP.ua.indexOf('ipad') > -1  ) ?
    true : false;

Код находиться «по запаху» агентом пользователя, на выбор для Android и IOS, если он есть. Добавьте его в конце POP.init, перед POP.resize ().

Потом, в resize function, если android или ios действительные, мы добавляем еще 50 пикселей в высоту документа — т.е. достаточно дополнительного пространства, чтобы иметь возможность прокрутить вниз адресную строку.

// this will create some extra space on the
// page, enabling us to scroll past
// the address bar, thus hiding it.
if (POP.android || POP.ios) {
    document.body.style.height = (window.innerHeight + 50) + 'px';
}

Обратите внимание, что мы делаем это только для мобильных устройств на Android и IOS, в противном случае у вас появятся неприятные полосы прокрутки. Кроме того, мы должны отложить запуск scrollTo, чтобы убедиться, что мобильный Safari не проигнорирует.

2.ПУСТЫЕ CANVAS

Теперь, когда мы масштабировали наш холст canvas ожидающий область просмотра, давайте добавим возможность сделать некоторые формы.

Примечание: В этом уроке мы будем придерживаться основных геометрических форм. IOS 5 и Chrome Beta для Android могут работать с большим количеством изображений спрайтов на высокой частоте кадров. Попробуйте это на Android 3.2 или ниже, и частота кадров будет падать в геометрической прогрессии. К счастью, не так много накладных расходов при циклах, так что у нас может быть множество пузырьков в нашей игре без ущерба для производительности игры при ее просмотре на старых мобильных устройствах.

Ниже, мы добавили основные Draw objects, которые позволяют очистить экран, нарисуйте прямоугольник и круг, и добавьте текст. Пока еще ничего умопомрачительного. Mozilla Developers Network как всегда имеет отличные ресурсы, и изобилует примерами для рисования на canvas.

// abstracts various canvas operations into
// standalone functions
POP.Draw = {

    clear: function() {
        POP.ctx.clearRect(0, 0, POP.WIDTH, POP.HEIGHT);
    },

    rect: function(x, y, w, h, col) {
        POP.ctx.fillStyle = col;
        POP.ctx.fillRect(x, y, w, h);
    },

    circle: function(x, y, r, col) {
        POP.ctx.fillStyle = col;
        POP.ctx.beginPath();
        POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true);
        POP.ctx.closePath();
        POP.ctx.fill();
    },

    text: function(string, x, y, size, col) {
        POP.ctx.font = 'bold '+size+'px Monospace';
        POP.ctx.fillStyle = col;
        POP.ctx.fillText(string, x, y);
    }

};

Наши Draw object имеют методы для очистки экрана и рисовки прямоугольников, кругов и текст. Преимущество абстрагирования этих операций заключается в том, что нам не нужно помнить точные вызовы холста canvas API, и теперь мы можем нарисовать круг с одной строки кода, а не пятью.

Давайте вставим его в тест:

// include this at the end of POP.init function
POP.Draw.clear();
POP.Draw.rect(120,120,150,150, 'green');
POP.Draw.circle(100, 100, 50, 'rgba(255,0,0,0.5)');
POP.Draw.text('Hello World', 100, 100, 10, '#000');

Включаем приведенный выше код в конце POP.init функции, и вы увидите пару форм на холсте canvas.

3. Волшебное прикосновение

Так же, как у нас есть click event, мобильные браузеры предоставляют методы для сенсорных event.

Интересная часть кода приведенного ниже – это  touchstart touchmove и touchend. При стандартном click event, мы можем получить координаты от e.pageX и e.pageY. Сенсорные event несколько отличаются. Они содержат массив touches, каждый элемент которого содержит сенсорный координаты и другие данные. Нам нужно только первое прикосновение, и мы получаем доступ к нему следующим образом: e.touches[0].

Примечание: Android обеспечивает наличие доступа, к сенсорным действиям только начиная с версии 4.

Мы также используем e.preventDefault();, когда вызывается каждый event, чтобы отключить прокрутку, масштабирование и любые другие действия, которые могут прервать поток игры.

Добавляем следующий код в POP.init function.

// listen for clicks
window.addEventListener('click', function(e) {
    e.preventDefault();
    POP.Input.set(e);
}, false);

// listen for touches
window.addEventListener('touchstart', function(e) {
    e.preventDefault();
    // the event object has an array
    // named touches; we just want
    // the first touch
    POP.Input.set(e.touches[0]);
}, false);
window.addEventListener('touchmove', function(e) {
    // we're not interested in this,
    // but prevent default behaviour
    // so the screen doesn't scroll
    // or zoom
    e.preventDefault();
}, false);
window.addEventListener('touchend', function(e) {
    // as above
    e.preventDefault();
}, false);

Вы, наверное, заметили, что приведенный выше код передает данные об event, на Input  object, который мы еще не определили. Давайте сделаем это сейчас:

// + add this at the bottom of your code,
// before the window.addEventListeners
POP.Input = {

    x: 0,
    y: 0,
    tapped :false,

    set: function(data) {
        this.x = data.pageX;
        this.y = data.pageY;
        this.tapped = true; 

        POP.Draw.circle(this.x, this.y, 10, 'red');
    }

};

Сейчас, давайте, опробуем его. Хм, круги не появляются. Потому что мы на холсте canvas, должны учитывать это при сопоставлении прикосновения к расположению экрана.

Во-первых, мы должны вычесть смещение координат.

var offsetTop = POP.canvas.offsetTop,
    offsetLeft = POP.canvas.offsetLeft;

this.x = data.pageX - offsetLeft;
this.y = data.pageY - offsetTop;

Затем, мы должны принять во внимание коэффициент, на который холст canvas было уменьшено, так что мы можем построить холст canvas по его фактическим размерам (который до сих пор 320 × 480).

var offsetTop = POP.canvas.offsetTop,
    offsetLeft = POP.canvas.offsetLeft;
    scale = POP.currentWidth / POP.WIDTH;

this.x = ( data.pageX - offsetLeft ) / scale;
this.y = ( data.pageY - offsetTop ) / scale;

Если ваша голова начинает болеть, практический пример должен немного помочь. Представьте себе, что пользователь стучит на холсте  500 × 750 выше на 400, 400. Нам нужно перевести его до 480 × 320. Таким образом, фактическая x координата разделена на 400. В данном случае, 400 ÷ 1,56 = 320.5.

Вместо расчета на каждое touch event, мы можем вычислить их после изменения размера. Добавьте следующий код в начало программы, наряду с другими переменными:

    // let's keep track of scale
    // along with all initial declarations
    // at the start of the program
    scale:  1,
    // the position of the canvas
    // in relation to the screen
    offset = {top: 0, left: 0},

В нашей функции изменения размера, после регулировки ширины холста и высоты, мы записываем текущий масштаб и смещение:

// add this to the resize function.
POP.scale = POP.currentWidth / POP.WIDTH;
POP.offset.top = POP.canvas.offsetTop;
POP.offset.left = POP.canvas.offsetLeft;

Теперь мы используем их в  set нашего POP.Input:

this.x = (data.pageX - POP.offset.left) / POP.scale;
this.y = (data.pageY - POP.offset.top) / POP.scale;

4.В цикле

Типичный цикл игры следующим образом:

  1. Запрос пользовательского ввода;
  2. Обновление символов и процесса столкновения;
  3. Визуализация символов на экране;
  4. Повтор.

Мы могли бы, конечно, использовать setInterval, но есть новая игрушка под названием requestAnimationFrame. Она обещает плавную анимацию и является более эффективным для батареи. Плохая новость состоит в том, что оно не поддерживается последовательно во всех мобильных браузерах. Но тут появился Paul Irish и помог всем с помощью удобного шима. 

Давайте пойдем дальше и добавить шим в начале нашего текущего кода.

// http://paulirish.com/2011/requestanimationframe-for-smart-animating
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
...

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.