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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('logo', 'assets/sprites/phaser3-logo-x2.png');
        this.load.image('lemming', 'assets/sprites/lemming.png');
    }

    create ()
    {

        const container0 = this.add.container(0, 0);
        const container1 = this.add.container(0, 0);
        const container2 = this.add.container(0, 0);
        const container3 = this.add.container(0, 0);

        const image0 = this.add.image(0, 0, 'lemming');
        const image1 = this.add.image(-100, -100, 'lemming');
        const image2 = this.add.image(100, -100, 'lemming');
        const image3 = this.add.image(100, 100, 'lemming');
        const image4 = this.add.image(-100, 100, 'lemming');

        for (let index = 0; index < 10; index++)
        {
            container0.add(this.add.image(Math.random() * 800, Math.random() * 600, 'lemming'));
            container1.add(this.add.image(Math.random() * 800, Math.random() * 600, 'lemming'));
            container2.add(this.add.image(Math.random() * 800, Math.random() * 600, 'lemming'));
            container3.add(this.add.image(Math.random() * 800, Math.random() * 600, 'lemming'));
        }

        container3.setScrollFactor(1.00);
        container2.setScrollFactor(0.75);
        container1.setScrollFactor(0.50);
        container0.setScrollFactor(0.25);

        this.time = 0.0;

    }

    update ()
    {
        this.cameras.main.scrollX = Math.cos(this.time) * 100;
        this.cameras.main.scrollY = Math.sin(this.time) * 100;
        this.time += 0.01;
    }
}

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

const game = new Phaser.Game(config);

Что такое Container и Scroll Factor?

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

Scroll Factor (фактор прокрутки) — это множитель, который определяет, насколько сильно объект или контейнер будет следовать за движением камеры. Значение `0означает, что объект закреплен на экране и не двигается при скролле камеры. Значение1— объект движется синхронно с камерой (стандартное поведение). Промежуточные значения, например0.5`, заставляют объект двигаться в два раза медленнее камеры, создавая эффект фонового слоя.

В предоставленном примере мы управляем не отдельными спрайтами, а целыми контейнерами, что делает код чище и производительнее.

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

В методе create() сцены инициализируется основная структура. Сначала создаются четыре пустых контейнера с помощью this.add.container(0, 0). Их начальная позиция в мире (0,0) не важна, так как позже мы будем управлять их движением через камеру.

const container0 = this.add.container(0, 0);
const container1 = this.add.container(0, 0);
const container2 = this.add.container(0, 0);
const container3 = this.add.container(0, 0);

Затем создаются пять отдельных спрайтов лемминга и добавляются в мировое пространство сцены (они не находятся в контейнерах). Это демонстрирует, что обычные объекты и объекты внутри контейнеров могут сосуществовать.

const image0 = this.add.image(0, 0, 'lemming');
const image1 = this.add.image(-100, -100, 'lemming');
// ... и так далее

Наполнение контейнеров и настройка Scroll Factor

Далее каждый из четырех контейнеров наполняется десятью случайно расположенными спрайтами. Обратите внимание: спрайты добавляются непосредственно в контейнер методом container.add(). Их позиции задаются относительно мировых координат, но теперь они становятся дочерними элементами контейнера.

for (let index = 0; index < 10; index++)
{
    container0.add(this.add.image(Math.random() * 800, Math.random() * 600, 'lemming'));
    // ... аналогично для container1, container2, container3
}

Самое важное — установка разного фактора прокрутки для каждого контейнера. Этот вызов применяется ко всему контейнеру и всем его дочерним элементам.

container3.setScrollFactor(1.00); // Движется синхронно с камерой
container2.setScrollFactor(0.75); // Движется чуть медленнее камеры
container1.setScrollFactor(0.50); // Движется в два раза медленнее
container0.setScrollFactor(0.25); // Движется в четыре раза медленнее

Анимируем камеру и наблюдаем результат

В методе update() происходит анимация движения основной камеры (this.cameras.main). Ее скролл по осям X и Y плавно изменяется по синусоидальному закону, создавая круговое движение.

this.cameras.main.scrollX = Math.cos(this.time) * 100;
this.cameras.main.scrollY = Math.sin(this.time) * 100;
this.time += 0.01;

Когда камера двигается, все объекты в мире сдвигаются. Однако контейнеры и их содержимое сдвигаются с разной скоростью, благодаря установленному scrollFactor. Контейнер с фактором 1.0 (container3) будет следовать за камерой так же, как и обычные спрайты, созданные вне контейнеров. Контейнер с фактором 0.25 (container0) будет едва смещаться, создавая эффект самого дальнего (или самого статичного) слоя. Это и есть основа параллакс-скроллинга.

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

Использование Container.setScrollFactor() — это мощный и производительный способ управления движением групп объектов. Вы можете легко создавать сложные многослойные фоны, интерфейсы, закрепленные на экране (scrollFactor: 0), или эффекты "привязанных" к игроку элементов. Для экспериментов попробуйте: изменить scrollFactor в реальном времени в ответ на события игры; вкладывать контейнеры друг в друга с разными факторами; применять этот метод не только к спрайтам, но и к тексту, тайловым слоям или частицам внутри контейнера.