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

TileSprite — это мощный игровой объект в Phaser, который позволяет использовать одно изображение в качестве повторяющейся текстуры для заполнения области любого размера. Его главная «фишка» — возможность динамически смещать эту текстуру, создавая эффекты бесконечного прокручивающегося фона, волн, воды или психоделических узоров. В этой статье мы разберем практический пример, где две текстуры движутся по кругу, создавая гипнотический визуальный эффект, и поймем, как управлять этим движением через свойства `tilePositionX` и `tilePositionY`.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    iter = 0;
    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');
        this.load.image('bunny', 'assets/sprites/bunny.png');
        this.load.bitmapFont('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');
    }

    create ()
    {
        this.image0 = this.add.tileSprite(400, 300, 500, 500, 'image0');
        this.add.sprite(400, 300, 'bunny');
        this.image1 = this.add.tileSprite(400, 300, 150, 150, 'image1');
        this.add.bitmapText(0, 0, 'desyrel', 'Hello World');
    }

    update ()
    {
        this.image0.tilePositionX = Math.cos(this.iter) * 400;
        this.image0.tilePositionY = Math.sin(this.iter) * 400;
        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 и зачем он нужен

В отличие от обычного Sprite, который просто отображает картинку в своих границах, TileSprite (плиточный спрайт) использует текстуру как узор, повторяя её (тайля) по всей своей площади. Это крайне эффективно для создания больших фоновых областей без использования гигантских изображений.

В нашем примере создаются два таких объекта: большой фон (image0) размером 500x500 пикселей с портретом и маленький вращающийся элемент (image1) размером 150x150 пикселей с изображением гриба. Оба центрированы на экране (координаты 400, 300).

Разбор сцены: создание объектов

В методе create() инициализируются все визуальные элементы сцены. Обратите внимание на порядок добавления: объекты, добавленные позже, отображаются поверх предыдущих.

create ()
{
    this.image0 = this.add.tileSprite(400, 300, 500, 500, 'image0');
    this.add.sprite(400, 300, 'bunny');
    this.image1 = this.add.tileSprite(400, 300, 150, 150, 'image1');
    this.add.bitmapText(0, 0, 'desyrel', 'Hello World');
}

Сначала создается фон image0. Затем обычный спрайт bunny (зайчик) добавляется на тот же слой, что и фон. После него создается второй TileSprite (image1), который будет отрисован поверх зайчика. В самом конце добавляется bitmap-текст в левый верхний угол. Именно такой порядок обеспечивает видимость гриба и текста поверх других элементов.

Магия движения: обновление tilePosition

Анимация достигается в методе update(), который вызывается на каждом кадре игры. Ключевую роль играют свойства tilePositionX и tilePositionY объекта TileSprite. Они задают смещение внутренней текстуры относительно левого верхнего угла спрайта.

update ()
{
    this.image0.tilePositionX = Math.cos(this.iter) * 400;
    this.image0.tilePositionY = Math.sin(this.iter) * 400;
    this.image1.tilePositionX = Math.cos(-this.iter) * 400;
    this.image1.tilePositionY = Math.sin(-this.iter) * 400;
    this.iter += 0.01;
}

Здесь переменная this.iter плавно увеличивается каждые кадр. Её значение подставляется в тригонометрические функции Math.cos и Math.sin, которые возвращают значения от -1 до 1. Умножив результат на 400, мы получаем смещение текстуры в диапазоне от -400 до 400 пикселей. Для image0 и image1 используются противоположные знаки у аргумента (this.iter и -this.iter), что заставляет их текстуры вращаться в противоположных направлениях, создавая сложный динамический паттерн.

Почему это работает: принцип тайлинга

Когда вы смещаете tilePosition, текстура не перемещается как цельное изображение. Вместо этого смещается её точка начала отрисовки. Поскольку текстура зациклена (затайлена), часть, «уехавшая» за одну границу, сразу появляется с противоположной стороны. Это создает иллюзию бесконечного, плавно скользящего полотна без разрывов.

Именно этот принцип используется для создания бегущих облаков, текущей воды или, как в нашем случае, абстрактного вращающегося фона. Изменяя множитель (в примере это 400) и скорость увеличения this.iter (0.01), можно контролировать амплитуду и скорость движения.

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

TileSprite с анимированными свойствами tilePosition — это простой и производительный способ добавить в игру «живые» фоны и эффекты. Для экспериментов попробуйте привязать смещение текстуры не ко времени, а к действиям игрока (например, к скорости персонажа для эффекта параллакса), использовать разные математические функции для движения или менять свойство tileScale для создания эффекта "приближения" текстуры.