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

При создании игр с динамическим интерфейсом или анимированным текстом часто возникает задача точного позиционирования и обработки столкновений. Стандартные методы получения размеров игрового объекта могут не учитывать текущий масштаб. В этой статье мы разберем, как корректно получать реальные, масштабированные границы объекта BitmapText в Phaser 3, что особенно полезно для выравнивания, проверки нажатий или отрисовки рамок вокруг текста.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
        this.string = 'Phaser 3\nBitmapText\nScaling\nwith bounds';
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
    }

    create ()
    {

        this.text = this.add.bitmapText(0, 0, 'atari', this.string)
            .setFontSize(32);

        this.graphics = this.add.graphics({ x: 0, y: 0, lineStyle: { thickness: 1, color: 0xffff00, alpha: 1 } });

        this.tweens.add({
            targets: this.text,
            duration: 4000,
            scaleX: 2,
            ease: 'Quad.easeInOut',
            repeat: -1,
            yoyo: true
        });

        this.tweens.add({
            targets: this.text,
            duration: 2000,
            scaleY: 4,
            ease: 'Quad.easeInOut',
            repeat: -1,
            yoyo: true
        });
    }

    update ()
    {
        this.text.setText(this.string + '\nScale X: ' + this.text.scaleX.toFixed(4));

        const bounds = this.text.getTextBounds();

        this.graphics.clear();
        this.graphics.strokeRect(bounds.global.x, bounds.global.y, bounds.global.width, bounds.global.height);
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Создание и анимация BitmapText

В примере создается сцена с объектом BitmapText. Загружается растровый шрифт, после чего текст добавляется на сцену с начальным размером 32 пикселя.

Особенность примера — применение двух независимых твинов для анимации масштабирования по осям X и Y с разной длительностью и поведением 'yo-yo' (повтор туда-обратно). Это приводит к постоянному и плавному изменению пропорций текста.

this.text = this.add.bitmapText(0, 0, 'atari', this.string).setFontSize(32);

this.tweens.add({
    targets: this.text,
    duration: 4000,
    scaleX: 2,
    ease: 'Quad.easeInOut',
    repeat: -1,
    yoyo: true
});

Метод getTextBounds() — ключ к решению

Свойства width и height объекта BitmapText часто хранят исходные, не масштабированные размеры. Для получения актуальных размеров с учетом текущего scaleX, scaleY, угла поворота и других трансформаций используется метод getTextBounds().

Этот метод возвращает объект bounds с комплексной информацией. Наиболее полезны для практического использования поля global, которые содержат мировые координаты и размеры уже трансформированного текстового блока.

const bounds = this.text.getTextBounds();
// bounds.global содержит: x, y, width, height

Визуализация границ и обновление в реальном времени

Чтобы наглядно видеть, как изменяются реальные границы текста, в примере используется объект Graphics. В функции update(), которая вызывается на каждом кадре, выполняются три действия: 1. Обновляется содержимое текста, чтобы вывести в нем текущее значение масштаба по X. 2. Получаются актуальные границы через getTextBounds(). 3. Старая графика очищается, и поверх текста рисуется новый желтый прямоугольник (strokeRect) по рассчитанным координатам и размерам.

update ()
{
    this.text.setText(this.string + '\nScale X: ' + this.text.scaleX.toFixed(4));

    const bounds = this.text.getTextBounds();

    this.graphics.clear();
    this.graphics.strokeRect(bounds.global.x, bounds.global.y, bounds.global.width, bounds.global.height);
}

Практическое применение

Получение масштабированных границ — это не только для отладки. Эта техника незаменима в реальных проектах: * **Точное выравнивание:** Для центрирования текста, который меняет размер, или размещения других элементов относительно его текущих границ. * **Кастомизация попаданий (Hit Area):** Чтобы область нажатия на текст соответствовала его видимому масштабированному виду, а не исходному прямоугольнику. * **Динамический UI:** При создании интерфейсов, где текстовые кнопки или подсказки могут анимировано увеличиваться или уменьшаться.

// Пример: проверка, находится ли курсор в границах масштабированного текста
if (bounds.global.contains(pointer.x, pointer.y)) {
    // Действие при наведении
}

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

Использование getTextBounds().global — правильный способ работы с размерами BitmapText в динамических сценах. Для экспериментов попробуйте привязать к этим границам другие объекты (например, частицы), создать эффект «подсветки» при наведении или реализовать систему всплывающих подсказок, размер которых адаптируется под изменяющийся текст.