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

Когда вы делаете игру, которая должна работать на разных экранах, важно не только растягивать контент, но и задавать разумные границы. Без ограничений игра может выглядеть растянутой на огромном мониторе или нечитаемой на крошечном окне браузера. В этом примере мы разберем, как использовать параметры `min` и `max` в конфигурации масштабирования Phaser (`Phaser.Scale`). Это позволит вашей игре сохранять удобный интерфейс и производительность, автоматически подстраиваясь под размер окна, но в рамках заданных вами пределов.

Версия 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('rain', 'assets/pics/thalion-rain.png');
        this.load.image('logo', 'assets/sprites/phaser3-logo-x2.png');
    }

    create ()
    {
        this.bg = this.add.tileSprite(0, 0, this.scale.width, this.scale.height, 'rain').setOrigin(0);
        this.logo = this.add.sprite(this.scale.width / 2, this.scale.height / 2, 'logo');

        this.scale.on('resize', this.resize, this);
    }

    resize (gameSize, baseSize, displaySize, resolution)
    {
        const width = gameSize.width;
        const height = gameSize.height;

        this.cameras.resize(width, height);

        this.bg.setSize(width, height);
        this.logo.setPosition(width / 2, height / 2);
    }
}

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

const game = new Phaser.Game(config);

Настройка Scale Manager: сердце адаптивности

Ключ к управлению размерами игры лежит в конфигурации свойства scale при создании экземпляра Phaser.Game. В данном примере используется режим Phaser.Scale.RESIZE, который динамически меняет размеры рендера игры под размер ее родительского элемента в HTML.

Особое внимание стоит уделить полям min и max. Они определяют абсолютные минимальные и максимальные размеры внутренней области отрисовки (так называемого "game size"), независимо от того, насколько большое или маленькое окно у пользователя.

scale: {
    mode: Phaser.Scale.RESIZE,
    parent: 'phaser-example',
    width: '100%',
    height: '100%',
    min: {
        width: 800,
        height: 600
    },
    max: {
        width: 1600,
        height: 1200
    }
}

Что это значит на практике? Если пользователь растянет окно браузера на весь экран с разрешением 4K, внутренний размер игры не превысит 1600x1200 пикселей. И наоборот, если окно будет очень маленьким, игра гарантированно не сожмется меньше 800x600, предотвращая "склеивание" интерфейса.

Обработка события 'resize': динамическое обновление сцены

Когда размер окна меняется, Scale Manager генерирует событие 'resize'. Чтобы игра корректно отреагировала на это изменение (например, пересчитала позиции элементов), мы должны подписаться на это событие.

В методе create() сцены мы добавляем слушатель. Важно передать контекст (this) третьим аргументом, чтобы внутри функции-обработчика this ссылался на текущую сцену.

create ()
{
    this.bg = this.add.tileSprite(0, 0, this.scale.width, this.scale.height, 'rain').setOrigin(0);
    this.logo = this.add.sprite(this.scale.width / 2, this.scale.height / 2, 'logo');

    this.scale.on('resize', this.resize, this);
}

Изначально мы создаем фон как TileSprite, который будет растягиваться на всю доступную область, и спрайт логотипа в центре экрана, используя актуальные размеры из this.scale.width и this.scale.height.

Функция resize: перестраиваем мир под новые размеры

Функция resize вызывается каждый раз при изменении размера. Она получает несколько параметров, но в данном примере используется только первый — gameSize. Этот объект содержит новые ширину и высоту внутренней области отрисовки, уже учтя ограничения min и max.

Логика обработки состоит из трех ключевых шагов:

1. **Обновление камер:** Все камеры в сцене должны быть перенастроены под новый размер. Для этого используется метод this.cameras.resize(width, height). 2. **Обновление фона:** Размер TileSprite с фоном меняется, чтобы он заполнил новую область. 3. **Перемещение элементов:** Ключевые игровые объекты (как логотип) перепозиционируются, чтобы остаться в центре.

resize (gameSize, baseSize, displaySize, resolution)
{
    const width = gameSize.width;
    const height = gameSize.height;

    this.cameras.resize(width, height);

    this.bg.setSize(width, height);
    this.logo.setPosition(width / 2, height / 2);
}

Без вызова this.cameras.resize() область видимости камеры останется прежней, что может привести к отображению только части контента или черных полей.

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

Использование min и max в настройках Scale Manager — это простой и эффективный способ обеспечить базовую адаптивность игры, защитив ее интерфейс от экстремальных размеров окна. Механизм работает автоматически: вам нужно лишь правильно обработать событие resize. **Идеи для экспериментов:** 1. Попробуйте изменить значения min и max в конфиге и понаблюдайте, как игра ведет себя при растягивании окна. 2. Добавьте в сцену больше UI-элементов (кнопки, панели здоровья) и реализуйте их перепозиционирование в функции resize, используя не только центр, но и углы экрана. 3. Исследуйте другие режимы масштабирования, например Phaser.Scale.FIT или Phaser.Scale.ENVELOP, и посмотрите, как они сочетаются с ограничениями по размеру.