О чем этот пример
Визуальные эффекты — мощный инструмент для оживления игрового мира. Одним из базовых, но крайне полезных приемов является зеркальное отражение и динамическое изменение свойств игровых объектов (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. Использовать другие математические функции для расчета свойств, чтобы добиться эффектов волны, рывка или хаотичного движения.
