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

Создание интерфейса, который корректно отображается на экранах любого размера — ключевая задача для разработчика игр. Классическое масштабирование спрайтов часто приводит к растягиванию углов и потере деталей, из-за чего UI выглядит дешево и неаккуратно. В этой статье мы разберем, как использовать технику Nine Slice в Phaser для создания интерфейсных элементов, которые масштабируются правильно, сохраняя четкие углы и пропорционально растягивая только центральные части.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.atlas('ui', 'assets/ui/nine-slice.png', 'assets/ui/nine-slice.json');
    }

    create ()
    {
        const panel = this.add.nineslice(0, 0, 'ui', 'PopupBackground400', 400, 275, 160, 160, 100, 100).setOrigin(0, 0);

        const image = this.add.image(400, 0, 'ui', 'PopupBackground400').setOrigin(0, 0);

        this.tweens.add({
            targets: panel,
            height: 600,
            duration: 6000,
            ease: 'sine.inout',
            yoyo: true,
            repeat: -1,
        });

        this.tweens.add({
            targets: image,
            displayHeight: 600,
            duration: 6000,
            ease: 'sine.inout',
            yoyo: true,
            repeat: -1,
        });
    }
}

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

const game = new Phaser.Game(config);

Что такое Nine Slice?

Nine Slice (или 9-slice scaling) — это техника масштабирования двумерных изображений, при которой спрайт мысленно делится на девять частей: четыре угла, четыре края и центральная область. При изменении размеров объекта углы остаются неизменными, края растягиваются только в одном направлении (вертикально или горизонтально), а центральная часть — в обоих.

Это идеально подходит для панелей, кнопок, окон диалогов и других UI-элементов, где важно сохранить четкость рамок и скругленных углов при любых размерах. В Phaser для работы с этой техникой используется специальный Game Object — NineSlice.

Создание Nine Slice объекта в Phaser

В приведенном примере для создания панели используется метод this.add.nineslice(). Его ключевое отличие от обычного this.add.image() — наличие параметров, определяющих размеры срезов.

Давайте посмотрим на вызов конструктора:

const panel = this.add.nineslice(0, 0, 'ui', 'PopupBackground400', 400, 275, 160, 160, 100, 100).setOrigin(0, 0);

Разберем аргументы по порядку: 1. **0, 0** — координаты X и Y для размещения объекта. 2. **'ui'** — ключ атласа текстур, загруженного в preload. 3. **'PopupBackground400'** — ключ конкретного кадра (фрейма) внутри атласа. 4. **400, 275** — начальная ширина и высота создаваемого объекта. 5. **160, 160** — ширина и высота левого верхнего угла. Эти значения определяют размер неизменяемой угловой зоны. 6. **100, 100** — ширина и высота правого нижнего угла. Вместе с предыдущими параметрами они задают размеры всех четырех углов и, косвенно, краев.

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

Проблема обычного масштабирования (Scaling)

Для сравнения рядом с Nine Slice панелью создается обычное изображение:

const image = this.add.image(400, 0, 'ui', 'PopupBackground400').setOrigin(0, 0);

Это стандартный спрайт. Когда мы меняем его свойство displayHeight (или displayWidth), масштабируется всё изображение целиком, включая углы и рамки. Это приводит к их искажению — они становятся размытыми, растянутыми или сплющенными, в зависимости от пропорций. Такой подход быстро выдает низкокачественный интерфейс.

Наглядная демонстрация с помощью Tween

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

Твин для Nine Slice панели анимирует стандартное свойство height:

this.tweens.add({
    targets: panel,
    height: 600,
    duration: 6000,
    ease: 'sine.inout',
    yoyo: true,
    repeat: -1,
});

Твин для обычного изображения анимирует свойство displayHeight, которое отвечает за визуальное масштабирование:

this.tweens.add({
    targets: image,
    displayHeight: 600,
    duration: 6000,
    ease: 'sine.inout',
    yoyo: true,
    repeat: -1,
});

Запустив этот код, вы сразу увидите разницу: панель Nine Slice будет плавно увеличиваться в высоту, при этом ее верхняя и нижняя рамки (края) растянутся, а скругленные углы сохранят свою идеальную форму. Обычное изображение будет выглядеть так, как будто его тянут за верх и низ — углы истончатся и деформируются.

Практические советы по использованию

1. **Подготовка ассетов:** Для Nine Slice нужен спрайт, углы и рамки которого имеют достаточный "запас" пикселей. Центральная часть может быть однотонной или с простым повторяющимся узором. 2. **Определение размеров срезов:** Параметры leftWidth, rightWidth, topHeight, bottomHeight (в примере задаются через размеры углов) — это самые важные значения. Они должны точно соответствовать пиксельным размерам угловых областей на вашем исходном изображении. Неправильные значения приведут к визуальным артефактам. 3. **Использование с контейнерами:** NineSlice объекты можно помещать внутрь Container вместе с текстом, иконками и другими элементами UI для построения сложных интерфейсов. 4. **Изменение размеров:** Меняйте свойства width и height объекта, а не scaleX/scaleY. Изменение масштаба (scale) также будет работать, но прямое управление размерами чаще бывает интуитивно понятнее для UI.

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

Nine Slice — это обязательный инструмент в арсенале разработчика, который заботится о качестве пользовательского интерфейса. Он решает конкретную проблему деформации элементов при масштабировании, обеспечивая профессиональный вид вашей игры. **Идеи для экспериментов:** 1. Попробуйте создать кнопку с помощью NineSlice и анимировать ее свойство width при наведении курсора, чтобы получить эффект "резиновой" кнопки с неизменными углами. 2. Скомбинируйте несколько NineSlice объектов разного цвета и размера, чтобы создать сложное модальное окно с заголовком, телом и нижней панелью действий. 3. Испытайте технику на спрайтах с более сложными рамками (например, с деревянной или каменной текстурой) и подберите оптимальные размеры срезов.