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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    iter = 0;
    tween;
    image1;
    image0;

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

    create ()
    {
        this.image0 = this.add.tileSprite(400, 300, 800, 600, 'image0');
        this.image1 = this.add.tileSprite(400, 300, 250, 250, 'image1');

        this.tween = this.tweens.addCounter({
            from: 1,
            to: 2,
            duration: 5000,
            ease: 'Sine.easeInOut',
            yoyo: true,
            repeat: -1
        });
    }

    update ()
    {
        this.image0.tilePositionX = Math.cos(this.iter) * 700;
        this.image0.tilePositionY = Math.sin(this.iter) * 500;

        this.image0.tileScaleX = this.tween.getValue();
        this.image0.tileScaleY = this.tween.getValue();

        this.image1.tilePositionX = Math.cos(-this.iter) * 400;
        this.image1.tilePositionY = Math.sin(-this.iter) * 400;

        this.iter += 0.01;
    }
}

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

const game = new Phaser.Game(config);

Что такое TileSprite?

TileSprite — это особый тип игрового объекта в Phaser, который рисует текстуру с эффектом тайлинга (повторения). В отличие от обычного Sprite, он не ограничен одним экземпляром изображения. Его можно мысленно представить как прямоугольную область, заполненную плитками одного изображения.

Главное преимущество — возможность управлять положением (tilePosition) и масштабом (tileScale) самой текстуры внутри этого спрайта. Сдвигая текстуру, мы создаем иллюзию движения, при этом сам спрайт остается на месте в игре. Это очень производительно и экономит ресурсы.

В нашем примере создаются два TileSprite: один на весь экран с портретом, второй — поменьше, со спрайтом гриба.

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

Создаются тайл-спрайты в методе create сцены. Ключевой метод — this.add.tileSprite(x, y, width, height, textureKey).

this.image0 = this.add.tileSprite(400, 300, 800, 600, 'image0');
this.image1 = this.add.tileSprite(400, 300, 250, 250, 'image1');

Здесь: * x, y — координаты центра спрайта на холсте. * width, height — размеры прямоугольной области, которую будет занимать спрайт. Для image0 это размеры всего игрового окна (800x600). * textureKey — ключ текстуры, загруженной в preload.

Также в create создается твин — плавно меняющееся значение для анимации масштаба.

this.tween = this.tweens.addCounter({
    from: 1,
    to: 2,
    duration: 5000,
    ease: 'Sine.easeInOut',
    yoyo: true,
    repeat: -1
});

tweens.addCounter создает твин для числового значения, которое будет циклично колебаться между 1 и 2 с плавным ускорением и замедлением (Sine.easeInOut).

Анимация в методе Update

Вся магия движения происходит в методе update, который вызывается на каждом кадре. Мы анимируем два свойства каждого TileSprite.

**1. Положение текстуры (tilePosition):** Сдвигаем начальную точку отрисовки текстуры, используя тригонометрические функции для плавного циклического движения.

this.image0.tilePositionX = Math.cos(this.iter) * 700;
this.image0.tilePositionY = Math.sin(this.iter) * 500;

Для image1 движение идет в противоположную фазу (-this.iter), создавая интересный визуальный контраст.

**2. Масштаб текстуры (tileScale):** Меняем масштаб повторяемой текстуры, используя значение из созданного ранее твина.

this.image0.tileScaleX = this.tween.getValue();
this.image0.tileScaleY = this.tween.getValue();

Значение this.tween.getValue() плавно меняется от 1 до 2 и обратно, заставляя текстуру фона периодически "зумироваться".

Переменная this.iter увеличивается на каждом кадре, выступая в роли угла для расчета синуса и косинуса.

this.iter += 0.01;

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

Код завершается стандартной для Phaser 3 конфигурацией игры. Обратите внимание, что в type указан Phaser.WEBGL для использования аппаратного рендеринга.

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

const game = new Phaser.Game(config);

Эта конфигурация создает игровой холст размером 800x600 пикселей с темно-серым фоном и инициирует создание экземпляра нашей сцены Example.

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

TileSprite — мощный и простой инструмент для создания сложной на вид анимации фона с минимальными затратами. Экспериментируйте: попробуйте разные функции для tilePosition (например, привяжите к скорости игрока для эффекта параллакса), используйте отдельные твины для масштаба по осям X и Y, или накладывайте несколько слоев TileSprite с разной скоростью движения для создания глубины. Это основа для динамичных фонов в платформерах, шутерах или аркадах.