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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload () 
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('star', 'assets/demoscene/star2.png');
        this.load.image('dude', 'assets/sprites/phaser-dude.png');
    }

    create () 
    {
        this.w = this.cameras.main.width;
        this.h = this.cameras.main.height;
    
        var bg = this.add.group({ key: 'star', frameQuantity: 300 });
    
        this.sky = new Phaser.Display.Color(120, 120, 255);
        this.space = new Phaser.Display.Color(0, 0, 0);
    
        this.player = this.add.sprite(this.w / 2, 0, 'dude');
    
        this.cameras.main.startFollow(this.player);
    
        var rect = new Phaser.Geom.Rectangle(0, -2 * this.h, this.w, 2 * this.h);
    
        Phaser.Actions.RandomRectangle(bg.getChildren(), rect);
    }

    update () 
    {
        this.player.y = (Math.cos(this.time.now / 1000) * (this.h - 10)) - this.h;

        var hexColor = Phaser.Display.Color.Interpolate.ColorWithColor(this.sky, this.space, -this.h * 2, this.player.y);
    
        this.cameras.main.setBackgroundColor(hexColor);
    }
}

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

const game = new Phaser.Game(config);

Настройка сцены и загрузка ресурсов

В методе preload мы загружаем два изображения: спрайт игрока (dude) и текстуру звезды (star), которая будет использована для создания фоновых частиц.

preload () 
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('star', 'assets/demoscene/star2.png');
    this.load.image('dude', 'assets/sprites/phaser-dude.png');
}

Создание мира: звезды, игрок и цвета

В create подготавливается игровое пространство. Сначала мы сохраняем ширину и высоту основной камеры в переменные this.w и this.h для удобства. Затем создается группа (Group) из 300 звезд, используя загруженную текстуру.

var bg = this.add.group({ key: 'star', frameQuantity: 300 });

Определяются два целевых цвета для интерполяции: светло-голубой (sky) для верхней части и черный (space) для нижней. Создается спрайт игрока и размещается по центру по горизонтали вверху экрана. Камера начинает следовать за игроком с помощью this.cameras.main.startFollow(this.player).

this.sky = new Phaser.Display.Color(120, 120, 255);
this.space = new Phaser.Display.Color(0, 0, 0);
this.player = this.add.sprite(this.w / 2, 0, 'dude');
this.cameras.main.startFollow(this.player);

Звезды случайным образом распределяются по большому прямоугольнику, который расположен выше видимой области, создавая эффект звездного поля.

var rect = new Phaser.Geom.Rectangle(0, -2 * this.h, this.w, 2 * this.h);
Phaser.Actions.RandomRectangle(bg.getChildren(), rect);

Анимация и магия интерполяции цвета

Вся динамика происходит в методе update. Положение игрока по оси Y задается косинусоидой, зависящей от времени, что заставляет его плавно колебаться между верхом и низом экрана.

this.player.y = (Math.cos(this.time.now / 1000) * (this.h - 10)) - this.h;

Самое интересное — расчет цвета фона. Функция Phaser.Display.Color.Interpolate.ColorWithColor плавно смешивает два цвета (sky и space) на основе текущей позиции игрока (this.player.y). Третий аргумент — минимальное значение диапазона (здесь -this.h * 2), четвертый — максимальное (здесь this.player.y). Таким образом, когда игрок внизу (значение Y ближе к 0), цвет ближе к черному (space), а когда наверху (значение Y меньше), цвет ближе к голубому (sky).

var hexColor = Phaser.Display.Color.Interpolate.ColorWithColor(this.sky, this.space, -this.h * 2, this.player.y);

Полученный цвет применяется как фон основной камеры.

this.cameras.main.setBackgroundColor(hexColor);

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

Мы реализовали плавную смену фона, которая реагирует на движение игрока, используя всего несколько строчек кода и встроенный API Phaser. Этот прием можно расширить: например, интерполировать между несколькими цветами для сложных градиентов, привязать изменение цвета не к позиции игрока, а к игровому времени для цикла дня и ночи, или менять цвет фона при переходе между уровнями. Экспериментируйте с параметрами интерполяции и источниками данных для создания уникальной атмосферы в вашей игре.