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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    rect;
    bottomRight;
    bottomLeft;
    topRight;
    topLeft;
    image;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('einstein', 'assets/pics/ra-einstein.png');
        this.load.image('ball', 'assets/sprites/blue_ball.png');
    }

    create ()
    {
        this.image = this.add.image(400, 300, 'einstein');
        this.topLeft = this.add.image(400,300, 'ball');
        this.topRight = this.add.image(400,300, 'ball');
        this.bottomLeft = this.add.image(400,300, 'ball');
        this.bottomRight = this.add.image(400,300, 'ball');

        this.image.setScale(0.5,0.5);

        this.rect = this.image.getBounds();

        this.topLeft.setPosition(this.rect.x,this.rect.y);
        this.topRight.setPosition(this.rect.x + this.rect.width, this.rect.y);
        this.bottomLeft.setPosition(this.rect.x, this.rect.y + this.rect.height);
        this.bottomRight.setPosition(this.rect.x + this.rect.width, this.rect.y + this.rect.height);

    }

    update ()
    {
        this.image.rotation += 0.01;

        this.rect = this.image.getBounds();

        this.topLeft.setPosition(this.rect.x,this.rect.y);
        this.topRight.setPosition(this.rect.x + this.rect.width,this.rect.y);
        this.bottomLeft.setPosition(this.rect.x,this.rect.y + this.rect.height);
        this.bottomRight.setPosition(this.rect.x + this.rect.width,this.rect.y + this.rect.height);
    }
}

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

// var line;
const game = new Phaser.Game(config);

Что делает метод getBounds()?

Метод getBounds() возвращает прямоугольник (объект типа Phaser.Geom.Rectangle), который описывает мировые границы игрового объекта. Этот прямоугольник учитывает положение (`x,y), масштаб (scaleX,scaleY) и поворот (rotation) объекта. Его свойстваx,y,widthиheight` позволяют точно определить область, которую объект занимает на экране.

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

// Получение границ изображения как прямоугольника
this.rect = this.image.getBounds();

Подготовка сцены и создание объектов

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

Обратите внимание, что изображение сразу масштабируется в два раза. Метод getBounds() учтет это изменение.

preload ()
{
    this.load.image('einstein', 'assets/pics/ra-einstein.png');
    this.load.image('ball', 'assets/sprites/blue_ball.png');
}

create ()
{
    // Создание основного изображения и маркеров
    this.image = this.add.image(400, 300, 'einstein');
    this.topLeft = this.add.image(400,300, 'ball');
    this.topRight = this.add.image(400,300, 'ball');
    this.bottomLeft = this.add.image(400,300, 'ball');
    this.bottomRight = this.add.image(400,300, 'ball');

    // Масштабирование основного изображения
    this.image.setScale(0.5,0.5);
}

Вычисление и визуализация углов

В функции create() происходит первичная расстановка маркеров. Мы получаем прямоугольник границ с помощью this.image.getBounds() и используем его свойства для расчета координат каждого угла.

* topLeft (левый верхний): (rect.x, rect.y) * topRight (правый верхний): (rect.x + rect.width, rect.y) * bottomLeft (левый нижний): (rect.x, rect.y + rect.height) * bottomRight (правый нижний): (rect.x + rect.width, rect.y + rect.height)

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

// Получаем актуальные границы
this.rect = this.image.getBounds();

// Позиционируем маркеры по углам
this.topLeft.setPosition(this.rect.x,this.rect.y);
this.topRight.setPosition(this.rect.x + this.rect.width, this.rect.y);
this.bottomLeft.setPosition(this.rect.x, this.rect.y + this.rect.height);
this.bottomRight.setPosition(this.rect.x + this.rect.width, this.rect.y + this.rect.height);

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

Вся магия происходит в функции update(), которая выполняется каждый кадр. Изображение медленно вращается (this.image.rotation += 0.01). При повороте его мировые границы меняются.

Поэтому мы должны постоянно пересчитывать прямоугольник this.rect, снова вызывая this.image.getBounds(). После этого позиции маркеров обновляются по тем же формулам. В результате синие шары всегда точно отмечают углы вращающегося и масштабированного изображения, наглядно демонстрируя работу метода.

update ()
{
    // Вращаем основное изображение
    this.image.rotation += 0.01;

    // КРИТИЧЕСКИ ВАЖНО: Получаем ОБНОВЛЕННЫЕ границы после поворота
    this.rect = this.image.getBounds();

    // Обновляем позиции маркеров на основе новых границ
    this.topLeft.setPosition(this.rect.x,this.rect.y);
    this.topRight.setPosition(this.rect.x + this.rect.width,this.rect.y);
    this.bottomLeft.setPosition(this.rect.x,this.rect.y + this.rect.height);
    this.bottomRight.setPosition(this.rect.x + this.rect.width,this.rect.y + this.rect.height);
}

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

Метод getBounds() — это ваш надежный способ получить точные мировые координаты любого игрового объекта в Phaser, независимо от его трансформаций. Он незаменим для реализации точных коллизий, привязки элементов интерфейса к объектам или создания сложных визуальных эффектов. **Идеи для экспериментов:** 1. Попробуйте анимировать не только вращение, но и масштаб (scale) или положение (`x,y`) изображения и понаблюдайте за поведением маркеров. 2. Используйте полученный прямоугольник rect для отрисовки дебажного контура вокруг объекта с помощью this.add.graphics(). 3. Реализуйте простую проверку столкновения "курсор-объект", используя getBounds() и метод contains объекта Phaser.Geom.Rectangle.