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

В разработке игр часто требуется размещать объекты с учётом сетки или пропорционально размерам экрана. Класс `Phaser.Structs.Size` предоставляет мощный инструмент для управления размерами с поддержкой режимов подгонки (FIT) и выравнивания по сетке (snap). В этой статье разберем, как использовать метод `setSnap()` для создания адаптивных интерфейсов и игровых элементов, которые автоматически выравниваются по заданному шагу.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const debug = this.add.graphics();
        const text = this.add.text(10, 540, '', { fill: '#00ff00' });

        const parent = new Phaser.Structs.Size(640, 480);
        const child = new Phaser.Structs.Size(320, 240, Phaser.Structs.Size.FIT, parent);

        child.setSnap(16);

        const draw = () =>
        {
            debug.clear().translateCanvas(10, 10);
            debug.lineStyle(1.5, 0xffff00).strokeRect(1, 1, parent.width, parent.height);
            debug.fillStyle(0x00ff00, 0.5).fillRect(1, 1, child.width, child.height);

            text.setText([
                `width: ${child.width}`,
                `height: ${child.height}`,
                `aspect ratio: ${child.aspectRatio}`
            ]);
        };

        this.input.on('pointermove', pointer =>
        {

            child.setSize(pointer.x, pointer.y);

            draw();

        });

        draw();
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Создание родительского и дочернего размеров

В основе примера лежит работа с двумя объектами Phaser.Structs.Size. Первый представляет родительский контейнер (например, игровую область или окно интерфейса), второй — дочерний элемент, размеры которого мы будем динамически менять.

const parent = new Phaser.Structs.Size(640, 480);
const child = new Phaser.Structs.Size(320, 240, Phaser.Structs.Size.FIT, parent);

Здесь создаётся родительский размер 640×480 пикселей и дочерний с начальным размером 320×240. Ключевой параметр — Phaser.Structs.Size.FIT, который гарантирует, что дочерний размер будет сохранять свои пропорции и умещаться внутри родительского. Это полезно для создания адаптивных элементов, которые не выходят за границы заданной области.

Включение выравнивания по сетке

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

child.setSnap(16);

В данном случае шаг сетки равен 16 пикселям. Если вы попытаетесь установить ширину, например, в 330 пикселей, она автоматически скорректируется до 336 (так как 336 кратно 16, а 330 — нет). Это особенно полезно для пиксель-арт игр или интерфейсов, где требуется точное позиционирование без "дробных" значений.

Динамическое обновление размеров

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

this.input.on('pointermove', pointer => {
    child.setSize(pointer.x, pointer.y);
    draw();
});

Метод setSize() принимает новые ширину и высоту, но благодаря ранее установленному setSnap(16) эти значения будут моментально округлены до ближайших, кратных 16. После обновления размеров вызывается функция draw(), которая перерисовывает визуальное представление на canvas.

Визуализация и отладка

Для наглядности в примере используется графика debug и текстовое поле text. Они помогают увидеть, как меняются размеры в реальном времени.

const draw = () => {
    debug.clear().translateCanvas(10, 10);
    debug.lineStyle(1.5, 0xffff00).strokeRect(1, 1, parent.width, parent.height);
    debug.fillStyle(0x00ff00, 0.5).fillRect(1, 1, child.width, child.height);
    text.setText([
        `width: ${child.width}`,
        `height: ${child.height}`,
        `aspect ratio: ${child.aspectRatio}`
    ]);
};

Жёлтый контур показывает границы родительского контейнера, а полупрозрачный зелёный прямоугольник — текущий размер дочернего объекта с учётом snap. В текстовом поле выводятся точные числовые значения: ширина, высота и соотношение сторон (оно сохраняется благодаря режиму FIT).

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

Использование Phaser.Structs.Size с методами setSnap() и режимом FIT позволяет легко создавать адаптивные и выровненные по сетке игровые элементы. Этот подход особенно полезен для интерфейсов, меню или объектов, требующих точного позиционирования. Для экспериментов попробуйте изменить шаг сетки (например, на 32 или 8), использовать другие режимы размера (например, Phaser.Structs.Size.ENVELOP) или применить snap к нескольким взаимосвязанным объектам для создания сложных компоновок.