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

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

Версия 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('eye', 'assets/pics/lance-overdose-loader-eye.png');
    }

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

        const sprite = this.add.image(200, 200, 'eye').setInteractive();

        sprite.setScale(-1, 1);
        sprite.setOrigin(0);
        
        container.add(sprite);

        sprite.on('pointerover', function ()
        {
            console.log('over');
            this.setTint(0xff0000);
        });

        sprite.on('pointerout', function ()
        {
            console.log('out');
            this.clearTint();
        });
    }
}

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

const game = new Phaser.Game(config);

Настройка сцены и загрузка ассетов

В методе preload() мы устанавливаем базовый URL для загрузки ресурсов и загружаем одно изображение. Это стандартный подход в Phaser для подготовки ассетов перед созданием сцены.

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

Создание контейнера и спрайта

В методе create() происходит основная инициализация игровых объектов. Сначала создается контейнер с помощью this.add.container(0, 0). Контейнер — это невидимый объект, который служит родителем для других объектов, позволяя трансформировать их (перемещать, масштабировать, вращать) как единую группу.

Затем создается спрайт — изображение eye — и сразу же делается интерактивным с помощью setInteractive(). Это необходимо для того, чтобы объект мог реагировать на события указателя (мыши или касания).

const container = this.add.container(0, 0);
const sprite = this.add.image(200, 200, 'eye').setInteractive();

Трансформация спрайта и добавление в контейнер

Ключевой момент здесь — применение отрицательного масштаба по оси X с помощью setScale(-1, 1). Это зеркально отражает спрайт по горизонтали.

Метод setOrigin(0) устанавливает точку начала координат спрайта в его левый верхний угол. Это важно, потому что при отрицательном масштабе спрайт будет "отражаться" относительно этой точки. Без изменения точки отсчета спрайт мог бы уйти за пределы видимой области.

Наконец, спрайт добавляется в контейнер методом container.add(sprite). Теперь все трансформации, примененные к контейнеру, будут влиять и на спрайт.

sprite.setScale(-1, 1);
sprite.setOrigin(0);
container.add(sprite);

Обработка событий указателя

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

Мы назначаем два обработчика событий: - pointerover: Срабатывает, когда курсор заходит на спрайт. В примере спрайт окрашивается в красный цвет (setTint(0xff0000)). - pointerout: Срабатывает, когда курсор уходит со спрайта. Окраска снимается (clearTint()).

Обратите внимание, что внутри функций-обработчиков this ссылается на сам спрайт, что позволяет легко менять его свойства.

sprite.on('pointerover', function ()
{
    console.log('over');
    this.setTint(0xff0000);
});

sprite.on('pointerout', function ()
{
    console.log('out');
    this.clearTint();
});

Конфигурация и запуск игры

Это стандартная конфигурация для запуска Phaser-игры. В объекте config задаются основные параметры: тип рендерера, размеры холста, цвет фона, ID родительского HTML-элемента и класс основной сцены. После создания экземпляра Phaser.Game игра запускается.

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

const game = new Phaser.Game(config);

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

Контейнеры в Phaser — это мощный инструмент для группировки объектов и применения к ним общих трансформаций, что упрощает управление сложными композициями. Как показал пример, даже при применении таких нестандартных преобразований, как отрицательное масштабирование, система интерактивности Phaser продолжает работать корректно. Для экспериментов попробуйте: 1. Добавить в контейнер несколько спрайтов и анимировать положение или вращение самого контейнера. 2. Применить отрицательный масштаб по оси Y или к обоим осям одновременно. 3. Сделать интерактивным сам контейнер и сравнить поведение событий с событиями на дочернем спрайте. 4. Использовать разные методы setInteractive для определения более точной или, наоборот, более простой зоны взаимодействия (например, в виде прямоугольника или геометрической фигуры).