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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    iter = 0;
    image;

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

    create ()
    {
        this.image = this.add.image(400, 300, 'einstein');

        this.time.addEvent({
            delay: 1000,
            loop: true,
            callback: () => this.image.flipX = !this.image.flipX
        });

        this.time.addEvent({
            delay: 2000,
            loop: true,
            callback: () => this.image.flipY = !this.image.flipY
        });
    }

    update ()
    {
        this.image.scaleX = Math.sin(this.iter);
        this.image.scaleY = Math.cos(this.iter);
        this.image.rotation = this.iter;
        this.image.x = 400 + Math.sin(this.iter * 10) * 200;
        this.iter += 0.001;
    }
}

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

const game = new Phaser.Game(config);

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

Класс Example, расширяющий Phaser.Scene, является основой нашего примера. В методе preload() мы настраиваем базовый URL для загрузки и загружаем одно изображение. Это стандартная процедура для подготовки ресурсов.

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

Создание объекта и настройка таймеров для отражения

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

this.image = this.add.image(400, 300, 'einstein');

Затем настраиваются два циклических таймерных события с помощью this.time.addEvent(). Первое, с интервалом в 1000 мс, каждую секунду инвертирует логическое свойство flipX объекта, отвечающее за горизонтальное отражение. Второе событие, с интервалом в 2000 мс, делает то же самое для свойства flipY (вертикальное отражение).

this.time.addEvent({
    delay: 1000,
    loop: true,
    callback: () => this.image.flipX = !this.image.flipX
});

this.time.addEvent({
    delay: 2000,
    loop: true,
    callback: () => this.image.flipY = !this.image.flipY
});

Динамическая трансформация в методе update

Сердце анимации находится в методе update(), который вызывается на каждом кадре. Здесь мы динамически изменяем несколько ключевых свойств изображения, используя тригонометрические функции от накопительной переменной iter.

- scaleX и scaleY: Масштабирование по осям. Использование sin и cos создает плавное, пульсирующее изменение размера. - rotation: Вращение объекта. Угол поворота линейно увеличивается. - `x: Позиция по горизонтали. Добавление синусоидального компонента с высокой частотой (iter * 10) и большой амплитудой (200`) создает эффект быстрого колебания вокруг центральной точки.

Переменная iter инкрементируется на каждом кадре, обеспечивая непрерывное изменение параметров анимации.

update ()
{
    this.image.scaleX = Math.sin(this.iter);
    this.image.scaleY = Math.cos(this.iter);
    this.image.rotation = this.iter;
    this.image.x = 400 + Math.sin(this.iter * 10) * 200;
    this.iter += 0.001;
}

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

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

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

const game = new Phaser.Game(config);

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

Комбинируя простые свойства вроде flipX, flipY, scale, rotation и x/y в методе update(), можно создавать сложные и визуально интересные анимации без использования спрайт-листов или тяжеловесных ассетов. Для экспериментов попробуйте: 1. Заменить Image на Sprite и привязать отражение к анимациям ходьбы. 2. Управлять свойствами flip не по таймеру, а в ответ на ввод пользователя (например, направление движения). 3. Использовать другие математические функции для расчета свойств, чтобы добиться эффектов волны, рывка или хаотичного движения.