О чем этот пример
Управление отображением текстур — ключевой навык при создании динамичных 2D-игр. В Phaser вы можете не просто выводить изображение на экран, но и кадрировать его, отражать по горизонтали или вертикали, а также менять эти параметры в реальном времени в ответ на действия игрока. Этот приём открывает огромные возможности для оптимизации (использование одного атласа для множества спрайтов), создания спецэффектов (например, подсветки области курсора) и реализации интерактивных элементов интерфейса. В этой статье мы разберём практический пример, где текстура зеркально отражается, кадрируется, а область кадрирования следует за курсором мыши.
Версия 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/pics/kris-jovo.jpg');
}
create ()
{
this.add.image(400, 300, 'pic').setAlpha(0.3).setFlipX(true);
this.bob = this.add.image(400, 300, 'pic').setFlipX(true);
this.graphics = this.add.graphics();
const cropWidth = 200;
const cropHeight = 100;
this.bob.setCrop(0, 0, cropWidth, cropHeight);
this.offset = this.bob.getTopLeft();
this.input.on('pointermove', pointer =>
{
this.bob.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
}
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0x00ff00);
this.graphics.strokeRect(this.offset.x + this.bob._crop.x, this.offset.y + this.bob._crop.y, this.bob._crop.width, this.bob._crop.height);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d88',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
Вся логика примера содержится в классе сцены Example. В методе preload мы загружаем одно изображение, которое будет использоваться как текстура. Обратите внимание на использование setBaseURL — это удобно для указания базового пути к ресурсам.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/kris-jovo.jpg');
}
Создание фона и интерактивного изображения
В методе create мы создаём два экземпляра одного и того же изображения. Первый (this.add.image(400, 300, 'pic')) служит полупрозрачным фоном с отражением по горизонтали (setFlipX(true)). Второй экземпляр сохраняется в свойство this.bob — это наш основной интерактивный объект, который также сразу отражается.
create ()
{
this.add.image(400, 300, 'pic').setAlpha(0.3).setFlipX(true);
this.bob = this.add.image(400, 300, 'pic').setFlipX(true);
...
}
Затем создаётся объект Graphics (this.graphics) для последующей отрисовки зелёной рамки вокруг области кадрирования.
Настройка кадрирования и привязка к курсору
Сначала для объекта bob устанавливается исходная область кадрирования (кроп) размером 200x100 пикселей, начиная с левого верхнего угла текстуры.
const cropWidth = 200;
const cropHeight = 100;
this.bob.setCrop(0, 0, cropWidth, cropHeight);
Ключевой момент — вычисление точки отсчета (this.offset) с помощью метода getTopLeft(). Этот метод возвращает мировые координаты левого верхнего угла игрового объекта bob. Эти координаты нужны для корректного перевода позиции курсора в координаты текстуры объекта.
Затем мы подписываемся на событие движения указателя (pointermove). В обработчике мы вычисляем новые координаты для области кадрирования так, чтобы её центр совпадал с позицией курсора относительно левого верхнего угла объекта bob.
this.input.on('pointermove', pointer =>
{
this.bob.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
Визуализация области кадрирования
Метод update вызывается на каждом кадре. Здесь мы сначала очищаем предыдущую отрисовку Graphics, задаём стиль линии и рисуем зелёный прямоугольник, который обводит текущую область кадрирования.
Важно: координаты для прямоугольника рассчитываются как сумма смещения объекта (this.offset) и внутренних координат кадрирования (this.bob._crop). Свойство _crop — это внутренний объект Phaser, содержащий текущие параметры кропа (x, y, width, height). Его использование напрямую, как в этом примере, допустимо для демонстрации, но в продакшн-коде стоит быть осторожным с приватными свойствами.
update ()
{
this.graphics.clear();
this.graphics.lineStyle(1, 0x00ff00);
this.graphics.strokeRect(this.offset.x + this.bob._crop.x, this.offset.y + this.bob._crop.y, this.bob._crop.width, this.bob._crop.height);
}
Конфигурация и запуск игры
В конце файла определяется стандартная конфигурация игры Phaser (config), в которой указывается тип рендерера, элемент-контейнер, размеры холста, цвет фона и наша сцена Example. Затем создаётся экземпляр игры.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d88',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Этот пример наглядно демонстрирует мощь и гибкость работы с текстурами в Phaser. Комбинируя методы setFlipX/setFlipY, setCrop и реагируя на ввод данных, вы можете создавать сложные визуальные взаимодействия.
**Идеи для экспериментов:**
1. Измените логику обработки события pointermove, чтобы кроп "прилипал" к курсору только при зажатой кнопке мыши.
2. Добавьте возможность изменять размер области кадрирования с помощью колёсика мыши.
3. Используйте несколько объектов с одной текстурой, но разными областями кадрирования, чтобы создать анимированный спрайт из texture atlas.
4. Поэкспериментируйте с setFlipY и одновременным отражением по обеим осям.
