О чем этот пример
Работа с изображениями — основа визуальной части игры. Часто требуется показать не всю текстуру целиком, а лишь её часть. Метод `setCrop()`, доступный для игровых объектов типа `Image`, позволяет динамически обрезать текстуру, создавая эффекты видоискателя, масок или плавного раскрытия картинки. В этой статье мы разберем, как работает этот метод на практическом примере, где область обрезки следует за курсором мыши.
Версия 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('pic', 'assets/pics/kris-jovo.jpg');
}
create ()
{
this.add.image(400, 300, 'pic').setAlpha(0.3);
this.bob = this.add.image(400, 300, 'pic');
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);
Загрузка и подготовка сцены
В методе preload() загружаем текстуру из внешнего источника. Важно: setBaseURL() задаёт базовый путь для всех последующих загрузок, что удобно для организации ассетов.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('pic', 'assets/pics/kris-jovo.jpg');
}
В методе create() создаются основные объекты. Первое изображение добавляется на сцену с прозрачностью 0.3 и служит фоновой подсказкой, показывая полную текстуру. Второе изображение this.bob — это основной объект, к которому будет применяться обрезка. Также создаётся объект Graphics для отрисовки зелёной рамки, визуализирующей область crop.
create ()
{
this.add.image(400, 300, 'pic').setAlpha(0.3);
this.bob = this.add.image(400, 300, 'pic');
this.graphics = this.add.graphics();
}
Принцип работы метода setCrop
Метод setCrop(x, y, width, height) обрезает текстуру игрового объекта Image. Параметры `xиyзадают координаты верхнего левого угла области обрезки **относительно текстуры самого изображения**, аwidthиheight` — её размеры.
Ключевой момент: координаты crop отсчитываются не от позиции изображения на сцене, а от его внутренней текстуры. Поэтому для корректного позиционирования области обрезки относительно мировой системы координат нужны дополнительные вычисления.
В примере мы сразу задаём начальную область обрезки размером 200x100 пикселей, начиная с левого верхнего угла текстуры.
const cropWidth = 200;
const cropHeight = 100;
this.bob.setCrop(0, 0, cropWidth, cropHeight);
Метод getTopLeft() возвращает мировые координаты точки привязки изображения (по умолчанию это его центр). Эти координаты сохраняются в this.offset и используются позже для пересчёта позиции курсора.
Динамическое изменение crop по движению мыши
Чтобы область обрезки следовала за курсором, мы подписываемся на событие pointermove. В обработчике события вычисляются новые координаты `xиyдляsetCrop`.
Алгоритм:
1. От координат курсора (`pointer.x`, `pointer.y`) отнимаем мировые координаты левого верхнего угла изображения (`this.offset.x`, `this.offset.y`). Это переводит мировые координаты в локальную систему координат текстуры.
2. Чтобы центр области обрезки находился под курсором, от полученных локальных координат отнимаем половину ширины и высоты области (`cropWidth / 2`, `cropHeight / 2`).
3. Вызываем `this.bob.setCrop()` с вычисленными координатами.
this.input.on('pointermove', pointer =>
{
this.bob.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
Таким образом, при каждом движении мыши текстура объекта this.bob перерисовывается, показывая только ту её часть, которая находится под виртуальным "окном".
Визуализация области обрезки с помощью Graphics
Чтобы визуально выделить текущую область crop на экране, в методе update() каждый кадр рисуется зелёная рамка. Сначала предыдущая графика очищается вызовом this.graphics.clear().
Координаты для отрисовки рамки рассчитываются следующим образом:
1. Берутся внутренние, "сырые" координаты области обрезки из свойств `this.bob._crop.x` и `this.bob._crop.y`. Обратите внимание, это внутреннее свойство объекта (`_crop`), а не публичный API.
2. К ним прибавляются мировые координаты верхнего левого угла изображения (`this.offset.x`, `this.offset.y`). Это переводит координаты crop из системы координат текстуры обратно в мировую систему координат сцены.
3. Метод `strokeRect()` рисует прямоугольник по полученным мировым координатам.
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
);
}
В результате зелёная рамка точно обрамляет видимую часть изображения на сцене.
Что попробовать дальше
Метод setCrop() — мощный инструмент для работы с текстурами в Phaser 3, позволяющий создавать динамические эффекты без подготовки отдельных изображений. Для экспериментов попробуйте: изменить форму области обрезки (например, на круг, используя маску), анимировать размеры cropWidth и cropHeight для эффекта "приближения", или привязать область обрезки не к курсору, а к движущемуся спрайту, создав эффект фонарика в тёмном уровне.
