Практический пример — HTML5-игры с автоматическим изменением размера

Введение

Летом 2010 года мы создали Sand Trap — игру, которую приняли в конкурсе HTML5-игр для мобильных телефонов. Но большинство мобильных телефонов либо отображали только часть игры, либо делали игру слишком маленькой, что делало ее совершенно невозможной. Поэтому мы взяли на себя задачу плавно адаптировать игру к любому разрешению. После небольшого перепрограммирования и использования идей, изложенных в этой статье, у нас была игра, которая масштабировалась в любом современном браузере, независимо от того, работал ли он на настольном компьютере или мобильном устройстве.

Скриншот полноэкранного режима thwack
скриншот меньшего размера в окне браузера

Этот подход хорошо сработал для Sand Trap, поэтому мы использовали тот же метод в нашей последней игре Thwack!. Игра автоматически настраивает разрешение экрана, чтобы оно соответствовало как полноэкранным, так и нестандартным размерам окон, как показано на скриншотах ниже.

Для реализации этого потребовалось использовать преимущества как CSS, так и JavaScript. Использование CSS для заполнения всего экрана тривиально, но CSS не позволяет поддерживать одинаковое соотношение ширины и высоты, чтобы предотвратить растяжение холста и игровой области. Вот тут-то и появляется JavaScript. Вы можете масштабировать элементы документа с помощью JavaScript и запускать изменение размера по событиям окна.

Подготовка страницы

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

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

<div id="gameArea">
  <canvas id="gameCanvas"></canvas>
  <div id="statsPanel"></div>
</div>

Когда у вас есть базовая структура документа, вы можете присвоить этим элементам несколько свойств CSS, чтобы подготовить их к изменению размера. Многие свойства CSS для «gameArea» управляются напрямую с помощью JavaScript, но чтобы они работали, настройте несколько других свойств CSS, начиная с родительского блока div gameArea:

#gameArea {
  position: absolute;
  left:     50%;
  top:      50%;
}

Это поместит верхний левый угол холста в центр экрана. Функция автоматического изменения размера JavaScript, описанная в следующем разделе, манипулирует дополнительными свойствами CSS, чтобы изменить размер игровой области и центрировать ее в окне.

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

В этом примере начните с игровой области высотой 300 пикселей и шириной 400 пикселей. Холст покрывает всю игровую область, а полупрозрачная панель статистики проходит вдоль нижней части высотой 24 пикселя, как показано на рисунке 1.

Размеры дочерних элементов gameArea в пикселях
Рис. 1. Размеры дочерних элементов gameArea в пикселях.

Перевод этих значений в проценты приводит к тому, что холст имеет 100 % по ширине и 100 % по высоте (для gameArea, а не для окна). Разделив 24 на 300, мы получим высоту панели статистики 8 %, а поскольку она закроет нижнюю часть игровой области, ее ширина также будет равна 100 %, как показано на рисунке 2.

Размеры дочерних элементов gameArea в процентах
Рисунок 2. Размеры дочерних элементов gameArea в процентах.

Теперь, когда вы определили размеры игровой области и ее дочерних элементов, вы можете объединить свойства CSS для двух внутренних элементов следующим образом:

#gameCanvas {
  width: 100%;
  height: 100%;
}
#statsPanel {
  position: absolute;
  width: 100%;
  height: 8%;
  bottom: 0;
  opacity: 0.8;
}

Изменение размера игры

Теперь вы готовы создать функцию для обработки изменяемого размера окна. Сначала возьмите ссылку на родительский элемент документа gameArea.

var gameArea = document.getElementById('gameArea');

Поскольку вас не волнует точная ширина или высота, следующая информация, которую вам нужно установить, — это соотношение ширины и высоты. Используя предыдущую ссылку на игровую область шириной 400 пикселей и высотой 300 пикселей, вы знаете, что хотите установить соотношение сторон 4 единицы в ширину и 3 единицы в высоту.

var widthToHeight = 4 / 3;

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

var newWidth = window.innerWidth;
var newHeight = window.innerHeight;

Точно так же, как вы определили желаемое соотношение ширины и высоты, теперь вы можете определить текущее соотношение ширины и высоты окна:

var newWidthToHeight = newWidth / newHeight;

Это позволяет вам решить, будет ли игра заполнять экран вертикально или горизонтально, как показано на рисунке 3.

Установка элемента gameArea в окне с сохранением соотношения сторон.
Рисунок 3. Размещение элемента gameArea в окне с сохранением соотношения сторон.

Если желаемая форма игровой области шире формы окна (а высота меньше), вам необходимо заполнить окно по горизонтали и оставить поля сверху и снизу. Аналогично, если желаемая форма игровой области выше формы окна (и ширина уже), вам необходимо заполнить окно вертикально и оставить поля слева и справа.

Для этого проверьте желаемое соотношение ширины и высоты с соотношением ширины и высоты текущего окна и внесите соответствующие изменения следующим образом:

if (newWidthToHeight > widthToHeight) {
  // window width is too wide relative to desired game width
  newWidth = newHeight * widthToHeight;
  gameArea.style.height = newHeight + 'px';
  gameArea.style.width = newWidth + 'px';
} else { // window height is too high relative to desired game height
  newHeight = newWidth / widthToHeight;
  gameArea.style.width = newWidth + 'px';
  gameArea.style.height = newHeight + 'px';
}

Теперь, когда вы отрегулировали ширину и высоту игровой области, вам нужно отцентрировать ее, разместив сверху отрицательный запас, равный половине высоты, и слева, равный половине ширины. Помните, что CSS уже размещает верхний левый угол элемента div gameArea точно в центре окна, поэтому игровая область центрируется в окне:

gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';

Вы также можете автоматически настроить размер шрифта. Если у вас есть все дочерние элементы, использующие em, вы можете просто установить для свойства CSS FontSize блока div gameArea значение, определяемое его размером.

gameArea.style.fontSize = (newWidth / 400) + 'em';

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

var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;

Таким образом, завершенная функция изменения размера может выглядеть примерно так:

function resizeGame() {
    var gameArea = document.getElementById('gameArea');
    var widthToHeight = 4 / 3;
    var newWidth = window.innerWidth;
    var newHeight = window.innerHeight;
    var newWidthToHeight = newWidth / newHeight;
    
    if (newWidthToHeight > widthToHeight) {
        newWidth = newHeight * widthToHeight;
        gameArea.style.height = newHeight + 'px';
        gameArea.style.width = newWidth + 'px';
    } else {
        newHeight = newWidth / widthToHeight;
        gameArea.style.width = newWidth + 'px';
        gameArea.style.height = newHeight + 'px';
    }
    
    gameArea.style.marginTop = (-newHeight / 2) + 'px';
    gameArea.style.marginLeft = (-newWidth / 2) + 'px';
    
    var gameCanvas = document.getElementById('gameCanvas');
    gameCanvas.width = newWidth;
    gameCanvas.height = newHeight;
}

Теперь вы хотите, чтобы эти настройки выполнялись автоматически при изменении размера окна или, в случае мобильных устройств, при изменении ориентации экрана. Обработайте эти события, попросив их вызвать функцию resizeGame() следующим образом:

window.addEventListener('resize', resizeGame, false);
window.addEventListener('orientationchange', resizeGame, false);

Если размер окна изменен слишком высоко или ориентация экрана вертикальная, вы устанавливаете ширину, равную 100% окна, а если размер окна изменяется слишком широко или ориентация экрана горизонтальная, вы устанавливаете высоту, равную 100% окна. Оставшийся размер определяется в соответствии с заранее определенным соотношением сторон ширины и высоты.

Краткое содержание

Gopherwood Studios использовала версии этой структуры для всех наших игр HTML5, и она оказалась очень полезной для поддержки нескольких разрешений экрана и различных мобильных устройств. Кроме того, благодаря полноэкранному браузеру наши веб-игры получают захватывающий опыт, более похожий на традиционные настольные игры, чем на многие браузерные игры. Мы с нетерпением ждем новых инноваций в веб-играх, поскольку HTML5 и веб-технологии продолжают развиваться.