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

Создание плавной, динамической графики — ключ к визуальной привлекательности игры. Класс `TileSprite` (или `Phaser.GameObjects.TileSprite`) в Phaser позволяет «замостить» игровое пространство текстурой и анимировать её смещение, что открывает двери для множества эффектов: от текущей воды и движущегося фона до сложных параллакс-слоёв. Эта статья на практическом примере разберёт, как создать несколько TileSprite, управлять их положением в реальном времени и комбинировать с другими объектами для создания живой, многослойной сцены.

Версия 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.CANVAS,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Что такое TileSprite?

В отличие от обычного спрайта (Sprite), который отображает текстуру в своём исходном размере, TileSprite (плиточный спрайт) повторяет (замостивает) свою текстуру, чтобы заполнить заданную при создании ширину и высоту. Это похоже на CSS-свойство background-repeat: repeat.

Главная его суперсила — возможность динамически управлять точкой, с которой начинается это «замощение», через свойства tilePositionX и tilePositionY. Сдвигая эту точку, мы создаём иллюзию движения самой текстуры, при этом размер и положение игрового объекта на сцене остаются неизменными.

this.add.tileSprite(x, y, width, height, textureKey);

Разбор примера: Загрузка и создание сцены

Код начинается с загрузки ресурсов в методе preload. Обратите внимание: используется this.load.setBaseURL() для указания базового пути, что удобно для примеров, но в реальном проекте пути будут относительными к вашей структуре.

В методе create создаётся сама сцена. Порядок добавления объектов важен: они отрисовываются в порядке их создания (последний — поверх всех).

// 1. Большой TileSprite с портретом Эйнштейна. Будет фоном.
this.image0 = this.add.tileSprite(400, 300, 500, 500, 'image0');
// 2. Обычный спрайт с кроликом. Расположен на том же месте, но поверх фона.
this.add.sprite(400, 300, 'bunny');
// 3. Меньший TileSprite с грибом. Будет поверх кролика.
this.image1 = this.add.tileSprite(400, 300, 150, 150, 'image1');
// 4. BitmapText. Добавлен последним, будет поверх всех.
this.add.bitmapText(0, 0, 'desyrel', 'Hello World');

Ключевой момент: мы сохраняем ссылки на TileSprite (this.image0, this.image1) в переменные экземпляра класса, чтобы иметь к ним доступ в методе update для анимации.

Сердце анимации: Метод update

Метод update вызывается на каждом кадре игры. Здесь и происходит «оживление» TileSprite.

Мы используем простую тригонометрию (Math.cos и Math.sin) от нарастающей переменной this.iter для вычисления новых координат смещения текстуры. Умножение на 400 задаёт радиус кругового движения текстуры.

// Смещаем текстуру большого фона по кругу.
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;

Изменяя tilePosition, мы не двигаем сам игровой объект, а лишь сдвигаем «видимую часть» его текстуры, создавая плавный эффект бесконечного прокручивания. Направление движения второго спрайта противоположно первому (-this.iter), что сразу добавляет сцене визуальной сложности и динамики.

Конфигурация игры и рендерер

В конце примера определяется конфигурационный объект и создаётся экземпляр игры. Обратите внимание на параметр type: Phaser.CANVAS. В данном примере явно указано использование Canvas рендерера. Phaser также поддерживает Phaser.WEBGL, который обычно быстрее, но может иметь ограничения по платформам.

const config = {
    type: Phaser.CANVAS, // Используемый рендерер
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d', // Цвет фона, виден за всеми объектами
    parent: 'phaser-example', // ID HTML-элемента-контейнера
    scene: Example // Основная сцена
};

const game = new Phaser.Game(config);

Цвет фона (backgroundColor) задаёт цвет, который будет отображаться в тех областях экрана, не закрытых игровыми объектами. В нашем примере он виден по краям, так как размер TileSprite (500x500) меньше размера игры (800x600).

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

TileSprite — мощный и гибкий инструмент для создания динамических фонов и эффектов. Изменяя tilePosition и tileScale, можно имитировать течение воды, движение облаков, бегущую дорожку или бесконечный космос. **Идеи для экспериментов:** 1. Замените тригонометрические функции на прибавление к tilePositionX в update, чтобы создать классическую горизонтальную прокрутку фона. 2. Испытайте свойство tileScaleX/`Y`, чтобы анимировать масштаб текстуры внутри спрайта. 3. Создайте 2-3 слоя TileSprite с разной скоростью смещения (this.iter += 0.005, +=0.02) для реализации эффекта параллакса. 4. Привяжите изменение tilePosition к скорости игрового персонажа для интерактивного фона.