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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    offset;
    graphics;
    bob;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('pic', 'assets/sprites/hotdog.png');
    }

    create ()
    {
        this.add.image(400, 300, 'pic').setAlpha(0.3).setScale(2);

        this.bob = this.add.image(400, 300, 'pic').setScale(2);

        this.graphics = this.add.graphics();

        const cropRect = new Phaser.Geom.Rectangle(0, 0, 100, 40);

        this.bob.setCrop(cropRect);

        this.offset = this.bob.getTopLeft();

        this.input.on(Phaser.Input.Events.POINTER_MOVE, pointer =>
        {

            this.bob.setCrop(
                (pointer.x - this.offset.x - cropRect.width) / 2,
                (pointer.y - this.offset.y - cropRect.height) / 2,
                cropRect.width,
                cropRect.height
            );

        });
    }

    update ()
    {
        this.graphics.clear();
        this.graphics.lineStyle(1, 0x00ff00);
        this.graphics.strokeRect(this.offset.x + (this.bob._crop.x * 2), this.offset.y + (this.bob._crop.y * 2), this.bob._crop.width * 2, this.bob._crop.height * 2);
    }
}

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

const game = new Phaser.Game(config);

Загрузка и подготовка сцены

В методе preload загружается изображение, которое будет использоваться для демонстрации. В create создается фон и основной спрайт для кадрирования.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('pic', 'assets/sprites/hotdog.png');
}
create ()
{
    // Полупрозрачный фон для наглядности
    this.add.image(400, 300, 'pic').setAlpha(0.3).setScale(2);
    // Основной спрайт, который будем кадрировать
    this.bob = this.add.image(400, 300, 'pic').setScale(2);
    // Графический слой для отрисовки рамки
    this.graphics = this.add.graphics();
}

Создание и применение начального кадрирования

Кадрирование определяет прямоугольную область текстуры, которая будет видна. Объект Phaser.Geom.Rectangle задает начальные координаты (x, y) и размеры (width, height) этой области. Важно: координаты кадрирования относятся к исходной текстуре, а не к масштабированному спрайту на сцене.

const cropRect = new Phaser.Geom.Rectangle(0, 0, 100, 40);
this.bob.setCrop(cropRect);

Метод getTopLeft() возвращает мировые координаты верхнего левого угла спрайта. Они нужны для корректного расчета позиции рамки кадрирования в методе update, так как setCrop работает с координатами текстуры.

this.offset = this.bob.getTopLeft();

Интерактивное изменение кадра

Чтобы кадрирование реагировало на движение мыши, подписываемся на событие POINTER_MOVE. В обработчике вычисляются новые координаты кадра. Координаты указателя переводятся в систему координат текстуры: из мировых координат курсора вычитается смещение спрайта (this.offset) и половина размеров кадра, а результат делится на масштаб спрайта (в данном случае 2). Так мы получаем координаты верхнего левого угла нового кадра для текстуры.

this.input.on(Phaser.Input.Events.POINTER_MOVE, pointer =>
{
    this.bob.setCrop(
        (pointer.x - this.offset.x - cropRect.width) / 2,
        (pointer.y - this.offset.y - cropRect.height) / 2,
        cropRect.width,
        cropRect.height
    );
});

Визуализация границ кадрирования

Для наглядности в методе update рисуется зеленая рамка, показывающая текущую область кадрирования в мировых координатах. Каждый кадр графика очищается, задается стиль линии и рисуется прямоугольник. Координаты и размеры для рамки берутся из внутреннего свойства _crop спрайта и конвертируются обратно в мировые координаты с учетом масштаба и смещения.

update ()
{
    this.graphics.clear();
    this.graphics.lineStyle(1, 0x00ff00);
    this.graphics.strokeRect(
        this.offset.x + (this.bob._crop.x * 2),
        this.offset.y + (this.bob._crop.y * 2),
        this.bob._crop.width * 2,
        this.bob._crop.height * 2
    );
}

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

Стандартная конфигурация игры. Обратите внимание на pixelArt: true, который отключает сглаживание текстур, что полезно для пиксель-арт графики.

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

const game = new Phaser.Game(config);

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

Метод setCrop — это гибкий инструмент для управления отображением текстур. Он открывает возможности для создания нестандартных UI-элементов (например, заполняемых шкал), анимаций (постепенное проявление), игровых механик (ограничение обзора) или мини-карт. Попробуйте изменить размер кадра cropRect, привязать его не к курсору, а к положению другого игрового объекта, или анимировать кадрирование с помощью таймлайнов Tween для создания плавных эффектов открытия/закрытия.