О чем этот пример

При создании игры на Phaser, которая должна корректно отображаться на разных устройствах, критически важно управлять масштабированием. Встроенный ScaleManager предлагает мощные инструменты. В этой статье мы разберем режим `Phaser.Scale.FIT` и научимся задавать жесткие минимальные и максимальные границы для игрового холста, предотвращая его чрезмерное сжатие или растяжение. Это полезно для сохранения читаемости интерфейса и качества графики на экранах с экстремальными соотношениями сторон.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('pic', 'assets/pics/zero-two.png');
    }

    create ()
    {
        this.add.image(0, 0, 'pic').setOrigin(0);

        console.log(this.scale);
    }
}

const config = {
    type: Phaser.AUTO,
    backgroundColor: '#2dab2d',
    scale: {
        mode: Phaser.Scale.FIT,
        parent: 'phaser-example',
        width: 800,
        height: 600,
        min: {
            width: 800,
            height: 600
        },
        max: {
            width: 1600,
            height: 1200
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Настройка ScaleManager в конфиге

Вся конфигурация масштабирования происходит в объекте scale при создании экземпляра Phaser.Game. Здесь мы определяем ключевые параметры, которые контролируют, как игра будет вписываться в родительский контейнер или окно браузера.

const config = {
    type: Phaser.AUTO,
    backgroundColor: '#2dab2d',
    scale: {
        mode: Phaser.Scale.FIT,
        parent: 'phaser-example',
        width: 800,
        height: 600,
        min: {
            width: 800,
            height: 600
        },
        max: {
            width: 1600,
            height: 1200
        }
    },
    scene: Example
};

Параметр mode: Phaser.Scale.FIT заставляет игру масштабироваться, чтобы целиком поместиться в отведенную область, добавляя при необходимости черные полосы (letterboxing). Параметры width и height (800x600) задают внутреннее, игровое разрешение — это координатное пространство, в котором вы размещаете объекты. Объекты min и max определяют абсолютные границы для физического размера canvas-элемента на странице. В данном примере игра никогда не будет меньше 800x600 пикселей и никогда не превысит 1600x1200 пикселей.

Как работает режим FIT с ограничениями

Алгоритм работы связки FIT с min и max можно описать так: сначала Phaser проверяет доступный размер родительского контейнера (parent). Затем, исходя из этого размера, он вычисляет масштаб для внутреннего разрешения 800x600, чтобы оно целиком вписалось в контейнер. После этого применяются ограничения min и max.

Например, если размер контейнера — 400x300, то расчетный размер для FIT был бы 400x300 (сохранение пропорций). Однако min.width: 800 блокирует это — итоговый размер canvas будет принудительно установлен в 800x600, и появятся полосы прокрутки или игра выйдет за границы маленького контейнера. Если же контейнер огромный, например, 2000x1500, режим FIT захотел бы растянуть игру до этих размеров, но ограничение max не позволит canvas стать больше 1600x1200, и вокруг игры появятся большие черные поля.

Этот подход гарантирует, что ваша игра, спроектированная под разрешение 800x600, не станет слишком мелкой на огромных мониторах и не будет пытаться безуспешно втиснуться в слишком маленькую область, сохраняя минимально приемлемый размер для взаимодействия.

Работа с координатами в сцене

Важно понимать, что заданные в конфиге width и height (800x600) — это и есть ваша игровая область, независимо от реального физического размера canvas. Все координаты, которые вы задаете при размещении объектов, отсчитываются от этой виртуальной системы.

create ()
{
    this.add.image(0, 0, 'pic').setOrigin(0);
    console.log(this.scale);
}

В этом коде мы размещаем изображение в точке (0, 0) и устанавливаем точку вращения (origin) в (0, 0) — левый верхний угол. Изображение займет всю внутреннюю игровую область 800x600. Реальный canvas может быть больше или меньше (в пределах min/max), но само изображение будет отмасштабировано, чтобы заполнить эти 800x600 игровых единиц. Вывод console.log(this.scale) полезен для отладки — он покажет текущий объект ScaleManager со всеми его свойствами, такими как реальная ширина/высота canvas, расчетный масштаб и другие.

Когда использовать min и max?

Установка жестких границ — это компромисс между адаптивностью и контролем над пользовательским опытом.

* **Минимальный размер (min)**: Защищает UI от "расползания" на очень маленьких экранах (например, на старых смартфонах в портретной ориентации). Без него кнопки и текст могут стать нефункционально мелкими. Устанавливайте min исходя из минимального комфортного размера интерфейса. * **Максимальный размер (max)**: Предотвращает растягивание растровой графики и спрайтов на сверхшироких или 4K мониторах, что может привести к заметной пикселизации и снижению производительности из-за обработки огромного canvas. Это также полезно для сохранения плотности игрового поля — на огромном экране игра может стать слишком "пустой".

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

Что попробовать дальше

Использование Phaser.Scale.FIT в связке с объектами min и max дает разработчику точный контроль над границами адаптивности игры. Это фундамент для создания презентабельного и стабильного отображения на большинстве устройств. Для экспериментов попробуйте: изменить режим на Phaser.Scale.ENVELOP и посмотреть на разницу в поведении; динамически менять значения min и max через this.scale.setMinMax() в ответ на действия игрока; или комбинировать этот подход с системой камер для создания responsive-интерфейса поверх игрового мира.