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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('lemming', 'assets/sprites/lemming.png');
    }

    create ()
    {
        const container = this.add.container(0, 300);

        const sprite = this.add.sprite(0, 0, 'lemming');

        const text = this.add.text(10, 10);

        container.add(sprite);

        this.tweens.add({
            targets: container,
            x: 800,
            duration: 3000,
            yoyo: true,
            repeat: -1,
            onUpdate: function ()
            {
                const p = container.localTransform.transformPoint(sprite.x, sprite.y);

                text.setText([
                    sprite.x,
                    sprite.y,
                    p.x,
                    p.y
                ]);
            }
        });
    }
}

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

const game = new Phaser.Game(config);

Проблема локальных координат

Контейнеры (Container) в Phaser — это мощный инструмент для группировки игровых объектов. Они позволяют управлять всей группой как единым целым: перемещать, масштабировать, вращать. Однако у этого удобства есть обратная сторона.

Координаты (`x,y`) любого дочернего объекта, добавленного в контейнер, отсчитываются не от левого верхнего угла сцены, а от точки (0, 0) самого контейнера. Это его локальная система координат.

const container = this.add.container(0, 300);
const sprite = this.add.sprite(0, 0, 'lemming');
container.add(sprite);

В этом примере спрайт sprite имеет координаты (0, 0) внутри контейнера container. Но на сцене контейнер сам находится в точке (0, 300). Где же на самом деле находится спрайт? Чтобы это выяснить, нужны его мировые координаты.

Решение: localTransform.transformPoint()

Phaser предоставляет элегантное решение через свойство localTransform контейнера. Это объект, представляющий матрицу трансформации контейнера (учитывающую его позицию, масштаб, поворот). Метод transformPoint() этой матрицы как раз преобразует точку из локальной системы координат контейнера в мировую (систему координат сцены).

const worldPoint = container.localTransform.transformPoint(sprite.x, sprite.y);

Метод принимает локальные координаты (sprite.x, sprite.y) и возвращает объект Phaser.Math.Vector2 со свойствами `xиy`, которые уже являются искомыми мировыми координатами. Это самый прямой и надежный способ получения глобальной позиции дочернего объекта.

Практический пример в действии

Давайте рассмотрим полный пример из исходника. Мы создаем контейнер, спрайт и текстовый объект для отладки. Затем запускаем бесконечный tween, который двигает контейнер по горизонтали.

create ()
{
    const container = this.add.container(0, 300);
    const sprite = this.add.sprite(0, 0, 'lemming');
    const text = this.add.text(10, 10);
    container.add(sprite);

    this.tweens.add({
        targets: container,
        x: 800,
        duration: 3000,
        yoyo: true,
        repeat: -1,
        onUpdate: function ()
        {
            const p = container.localTransform.transformPoint(sprite.x, sprite.y);
            text.setText([ sprite.x, sprite.y, p.x, p.y ]);
        }
    });
}

Ключевой момент — функция onUpdate в tween'е. На каждом кадре анимации она вычисляет мировые координаты спрайта `pи выводит в текстовое поле как локальные (sprite.x,sprite.y), так и глобальные (p.x,p.y`) значения. Вы увидите, что локальные координаты спрайта остаются постоянными (0, 0), в то время как мировые — непрерывно меняются вслед за движением контейнера.

Важные нюансы и ограничения

1. **Трансформации контейнера:** Метод localTransform.transformPoint() автоматически учитывает не только позицию (`x,y) контейнера, но и его масштаб (scaleX,scaleY) и угол поворота (rotation`). Если вы применяете эти трансформации к контейнеру, они будут корректно отражены в конечных мировых координатах. 2. **Производительность:** Вызов этого метода на каждом кадре (onUpdate, update) — операция быстрая, но если у вас сотни объектов, стоит задуматься об оптимизации, например, кэшировании результата, если позиция контейнера не изменилась. 3. **Альтернативный подход getWorldTransformMatrix:** Для более сложных случаев, когда нужно преобразовать много точек или получить полную матрицу трансформации, можно использовать container.getWorldTransformMatrix(). Однако для простого получения позиции одного объекта transformPoint() — идеальный выбор.

// Альтернативный, более сложный способ
const matrix = container.getWorldTransformMatrix();
const worldPoint = matrix.transformPoint(sprite.x, sprite.y);

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

Использование container.localTransform.transformPoint() — это стандартный и эффективный способ перевода локальных координат дочернего объекта в глобальные. Этот метод решает частую проблему позиционирования при работе с контейнерами. **Идеи для экспериментов:** 1. Попробуйте добавить в контейнер несколько спрайтов и в реальном времени выводить мировые координаты каждого из них. 2. Усложните анимацию контейнера, добавив вращение (rotation) и масштабирование (scale). Убедитесь, что вычисляемые мировые координаты по-прежнему точно указывают на положение спрайта на экране. 3. Используйте полученные мировые координаты для проверки пересечения (Overlap) с другим объектом на сцене, не входящим в контейнер, с помощью this.physics.overlap().