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

В разработке игр часто нужны панели, кнопки или рамки, которые могут плавно менять размер, но при этом сохранять чёткие углы и края без видимого растягивания. Техника "nine slice" (девять слайсов) решает именно эту задачу. В Phaser она реализована через метод `nineSlice` у `RenderTexture`, что позволяет создавать динамические UI-элементы на лету, используя минимальный набор текстур. Эта статья покажет, как загрузить необходимые фрагменты изображения и собрать из них масштабируемый объект, который будет идеально выглядеть при любых размерах. Это особенно полезно для диалоговых окон, инвентарей или любых элементов интерфейса, размер которых может меняться в процессе игры.

Версия 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.setPath('assets/tests/9slice');
        this.load.image('topLeft', 'topLeft.png');
        this.load.image('top', 'top.png');
        this.load.image('topRight', 'topRight.png');
        this.load.image('botLeft', 'botLeft.png');
        this.load.image('bot', 'bot.png');
        this.load.image('botRight', 'botRight.png');
        this.load.image('left', 'left.png');
        this.load.image('right', 'right.png');
    }

    create ()
    {
        const rt = this.add.renderTexture(50, 50, 512, 256);

        rt.nineSlice({
            topLeft: 'topLeft',
            topBackground: 'top',
            topRight: 'topRight',
            botLeft: 'botLeft',
            botBackground: 'bot',
            botRight: 'botRight',
            left: 'left',
            right: 'right'
        });
    }
}

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

const game = new Phaser.Game(config);

Подготовка текстур: зачем нужно девять частей

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

В примере загружаются восемь текстур: все части, кроме центра. Центр в данном случае не загружается отдельно, так как по умолчанию он остаётся прозрачным или заполняется цветом фона RenderTexture. Загрузка происходит в методе preload с указанием базового URL и пути.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.setPath('assets/tests/9slice');
this.load.image('topLeft', 'topLeft.png');
this.load.image('top', 'top.png');
this.load.image('topRight', 'topRight.png');
this.load.image('botLeft', 'botLeft.png');
this.load.image('bot', 'bot.png');
this.load.image('botRight', 'botRight.png');
this.load.image('left', 'left.png');
this.load.image('right', 'right.png');

Создание RenderTexture и применение Nine Slice

Основная магия происходит в методе create. Сначала создаётся объект RenderTexture с заданными координатами, шириной и высотой. RenderTexture — это особый игровой объект, который действует как динамический холст для рисования.

const rt = this.add.renderTexture(50, 50, 512, 256);

Затем к этой текстуре применяется метод nineSlice. В него передаётся объект-конфигурация, где ключи соответствуют частям изображения, а значения — это ключи загруженных ранее текстур. Обратите внимание, что для верхнего и нижнего фона используются ключи topBackground и botBackground, которые ссылаются на текстуры сторон.

rt.nineSlice({
    topLeft: 'topLeft',
    topBackground: 'top',
    topRight: 'topRight',
    botLeft: 'botLeft',
    botBackground: 'bot',
    botRight: 'botRight',
    left: 'left',
    right: 'right'
});

После вызова метода nineSlice текстура сразу отрисовывается на экране в виде готовой рамки размером 512x256 пикселей.

Как работает конфигурация Nine Slice

Каждый ключ в объекте, передаваемом в rt.nineSlice(), отвечает за свою зону итогового изображения. Важно понимать логику: - topLeft, topRight, botLeft, botRight — это углы. Они занимают место согласно своему исходному размеру и не растягиваются. - topBackground и botBackground — это верхняя и нижняя стороны. Они будут растянуты по горизонтали, чтобы заполнить пространство между углами. - left и right — левая и правая стороны. Они растягиваются по вертикали.

Центральная область (middle) в данном примере не задана, поэтому остаётся пустой (прозрачной). Если бы нужно было заполнить центр, можно было бы добавить текстуру и указать её в ключе middle. Размеры RenderTexture определяют итоговые габариты собранного объекта: система автоматически рассчитывает, как растянуть стороны, чтобы заполнить заданную ширину и высоту, сохраняя углы неизменными.

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

Одно из ключевых преимуществ использования RenderTexture с nineSlice — возможность динамически менять размер и перерисовывать элемент. Например, если размеры RenderTexture изменить после создания, рамка автоматически не обновится. Нужно очистить текстуру и вызвать nineSlice снова с новыми параметрами или в новом размере.

// Пример изменения размера и перерисовки
rt.clear(); // Очищаем RenderTexture
rt.setSize(600, 300); // Меняем размер
rt.nineSlice({
    // ... конфигурация слайсов
});

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

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

Метод nineSlice в Phaser — это мощный и элегантный способ создания масштабируемых графических элементов для интерфейсов. Он избавляет от необходимости создавать отдельные текстуры для каждого возможного размера панели или кнопки. Для экспериментов попробуйте: 1. Добавить текстуру для центральной области (middle) и создать полноценную плитку. 2. Анимировать изменение ширины и высоты RenderTexture с последующей перерисовкой nineSlice. 3. Комбинировать несколько RenderTexture с разными стилями рамок для сложных UI-компоновок. 4. Использовать эту технику для создания динамических health bar или индикаторов прогресса со стилизованными краями.