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

В игровой разработке часто требуется знать точные границы игровых объектов для обработки столкновений, позиционирования UI или динамических эффектов. Метод `getBounds()` в Phaser 3 — это мощный инструмент, который возвращает прямоугольник, описывающий мировые координаты объекта с учётом его трансформаций (позиции, масштаба, поворота). В этой статье мы разберем практический пример, который наглядно демонстрирует, как работает этот метод с изображениями внутри контейнера, и как визуализировать эти границы в реальном времени.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    bounds3;
    bounds2;
    bounds1;
    image3;
    image2;
    image1;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
        this.load.image('disk', 'assets/sprites/copy-that-floppy.png');
        this.load.image('tetris', 'assets/sprites/tetrisblock1.png');
    }

    create ()
    {
        this.image1 = this.add.image(700, 200, 'eye');
        this.image2 = this.add.image(180, 180, 'tetris');
        this.image3 = this.add.image(400, 500, 'disk');

        this.image1.setOrigin(1);
        this.image2.setOrigin(0);
        this.image3.setOrigin(0.5);

        this.image3.setScale(0.5);

        const container = this.add.container(100, 0, [ this.image1, this.image2, this.image3 ]).setAngle(20);

        this.graphics = this.add.graphics();

        this.bounds1 = this.image1.getBounds();
        this.bounds2 = this.image2.getBounds();
        this.bounds3 = this.image3.getBounds();

        this.tweens.add({

            targets: this.image3,
            duration: 2000,
            scaleX: 2,
            scaleY: 2,
            ease: 'Sine.easeInOut',
            repeat: -1,
            yoyo: true

        });
    }

    update ()
    {
        this.image1.rotation += 0.013;
        this.image2.rotation += 0.015;
        this.image3.rotation -= 0.010;

        this.bounds1 = this.image1.getBounds();
        this.bounds2 = this.image2.getBounds();
        this.bounds3 = this.image3.getBounds();

        this.graphics.clear();

        this.graphics.lineStyle(1, 0xff0000);
        this.graphics.strokeRectShape(this.bounds1);

        this.graphics.lineStyle(1, 0xffff00);
        this.graphics.strokeRectShape(this.bounds2);

        this.graphics.lineStyle(1, 0x00ff00);
        this.graphics.strokeRectShape(this.bounds3);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и загрузка ресурсов

Вся логика примера находится внутри класса сцены Example. В методе preload() мы загружаем три текстуры с помощью this.load.image(). Обратите внимание на использование setBaseURL() — это удобный способ указать базовый путь для всех загружаемых ресурсов.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
    this.load.image('disk', 'assets/sprites/copy-that-floppy.png');
    this.load.image('tetris', 'assets/sprites/tetrisblock1.png');
}

Создание объектов и настройка их свойств

В методе create() создаются три изображения (this.add.image) с разными начальными позициями. Ключевой момент — установка точки вращения (origin). Для image1 установлен origin в (1, 1) — правый нижний угол, для image2 — (0, 0) — левый верхний угол, а для image3 — (0.5, 0.5) — центр. Это повлияет на их вращение и расчёт границ. Объект image3 также сразу масштабируется вполовину.

create ()
{
    this.image1 = this.add.image(700, 200, 'eye');
    this.image2 = this.add.image(180, 180, 'tetris');
    this.image3 = this.add.image(400, 500, 'disk');

    this.image1.setOrigin(1);
    this.image2.setOrigin(0);
    this.image3.setOrigin(0.5);

    this.image3.setScale(0.5);
}

Работа с контейнером и получение границ

Далее все три изображения помещаются в контейнер (this.add.container). Контейнер позиционируется в точке (100, 0) и поворачивается на 20 градусов (setAngle(20)). Важно понимать, что контейнер применяет свои трансформации ко всем своим дочерним элементам. Сразу после создания объектов мы впервые получаем их границы с помощью метода getBounds() и сохраняем их в свойствах bounds1, bounds2, bounds3. Для image3 также создается бесконечная анимация пульсации масштаба с помощью this.tweens.add.

const container = this.add.container(100, 0, [ this.image1, this.image2, this.image3 ]).setAngle(20);

this.graphics = this.add.graphics();

this.bounds1 = this.image1.getBounds();
this.bounds2 = this.image2.getBounds();
this.bounds3 = this.image3.getBounds();

// Tween для масштабирования image3
this.tweens.add({
    targets: this.image3,
    duration: 2000,
    scaleX: 2,
    scaleY: 2,
    ease: 'Sine.easeInOut',
    repeat: -1,
    yoyo: true
});

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

Сердце примера — метод update(), который вызывается каждый кадр. Здесь происходит три вещи: объекты вращаются с разной скоростью, их границы пересчитываются через getBounds(), и эти границы отрисовываются с помощью графического объекта Graphics. this.graphics.clear() стирает рисунок предыдущего кадра. Затем для каждого объекта устанавливается стиль линии (lineStyle) разного цвета и рисуется прямоугольник (strokeRectShape) поверх изображения, используя рассчитанные границы.

update ()
{
    // Вращение объектов
    this.image1.rotation += 0.013;
    this.image2.rotation += 0.015;
    this.image3.rotation -= 0.010;

    // Пересчёт границ
    this.bounds1 = this.image1.getBounds();
    this.bounds2 = this.image2.getBounds();
    this.bounds3 = this.image3.getBounds();

    // Очистка и отрисовка новых границ
    this.graphics.clear();

    this.graphics.lineStyle(1, 0xff0000);
    this.graphics.strokeRectShape(this.bounds1);

    this.graphics.lineStyle(1, 0xffff00);
    this.graphics.strokeRectShape(this.bounds2);

    this.graphics.lineStyle(1, 0x00ff00);
    this.graphics.strokeRectShape(this.bounds3);
}

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

Метод getBounds() — это основа для многих игровых механик в Phaser 3. Как мы увидели, он корректно учитывает все трансформации: позицию, масштаб, поворот объекта, а также трансформации его родительского контейнера. Это делает его идеальным для проверки пересечений «по bounding box» или для точного позиционирования элементов интерфейса относительно игровых объектов. Для экспериментов попробуйте: изменить origin у объектов и посмотреть, как «прыгают» границы; добавить в контейнер скейл или сдвиг; использовать полученные границы для простой проверки столкновения между image1 и image2 с помощью Phaser.Geom.Rectangle.Overlaps.