О чем этот пример
Управление отображением текстур — ключевой навык для создания динамичной графики в играх. Этот пример демонстрирует, как с помощью методов `setCrop()` и `setFlip()` можно интерактивно обрезать и зеркально отражать кадр из текстурного атласа, открывая возможности для анимации, эффектов повреждений или интерактивных элементов интерфейса. Мы разберем код, который позволяет в реальном времени перемещать область обрезки с помощью указателя мыши.
Версия 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.atlas('atlas', 'assets/atlas/megaset-2.png', 'assets/atlas/megaset-2.json');
}
create ()
{
this.add.image(400, 300, 'atlas', 'hello').setAlpha(0.3).setFlipX(true);
this.graphics = this.add.graphics();
this.bob = this.add.image(400, 300, 'atlas', 'hello').setFlipX(true);
const cropWidth = 200;
const cropHeight = 100;
this.bob.setCrop(20, 20, 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() загружается текстурный атлас — эффективный способ хранения множества изображений в одном файле. Метод this.load.atlas() принимает ключ ресурса, путь к изображению и путь к JSON-файлу с данными о кадрах.
this.load.atlas('atlas', 'assets/atlas/megaset-2.png', 'assets/atlas/megaset-2.json');
В create() создается фоновое полупрозрачное изображение с ключом 'atlas' и конкретным кадром 'hello'. Метод setFlipX(true) зеркально отражает его по горизонтали. Это демонстрирует, как можно быстро модифицировать спрайт. Затем создается графический объект this.graphics для отладки и основной спрайт this.bob, с которым мы будем работать.
Инициализация обрезки (Crop)
Обрезка позволяет показывать только часть текстуры спрайта. Метод setCrop() объекта Image принимает четыре параметра: координаты X и Y верхнего левого угла области обрезки относительно текстуры, а также ее ширину и высоту.
const cropWidth = 200;
const cropHeight = 100;
this.bob.setCrop(20, 20, cropWidth, cropHeight);
Здесь мы устанавливаем начальную область обрезки размером 200x100 пикселей, смещенную на 20 пикселей от края текстуры. Важно: координаты обрезки (_crop.x, _crop.y) отсчитываются от внутренней системы координат текстуры, а не от позиции спрайта на сцене. Чтобы правильно связать их с мировыми координатами, нам нужна точка отсчета, которую мы получаем с помощью this.bob.getTopLeft() и сохраняем в this.offset.
Интерактивное управление обрезкой
Для создания интерактивности мы подписываемся на событие движения указателя pointermove. В обработчике события мы каждый раз пересчитываем позицию области обрезки так, чтобы ее центр следовал за курсором мыши.
this.input.on('pointermove', pointer => {
this.bob.setCrop(
(pointer.x - this.offset.x) - cropWidth / 2,
(pointer.y - this.offset.y) - cropHeight / 2,
cropWidth,
cropHeight
);
});
Логика расчета:
1. `pointer.x - this.offset.x` переводит мировые координаты курсора в координаты относительно левого верхнего угла спрайта.
2. Вычитание `cropWidth / 2` и `cropHeight / 2` центрирует область обрезки под курсором.
3. Ширина и высота области остаются фиксированными. Таким образом, при движении мыши видимая часть текстуры `this.bob` меняется.
Визуализация области обрезки
Чтобы визуально отображать текущие границы обрезки, в методе 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);
Ключевой момент: итоговые экранные координаты прямоугольника — это сумма смещения спрайта (this.offset) и внутренних координат обрезки (this.bob._crop). Свойства _crop (например, _crop.x) обновляются автоматически при каждом вызове setCrop(). Метод graphics.clear() вызывается каждый кадр, чтобы стереть предыдущий прямоугольник перед рисованием нового.
Конфигурация игры и запуск
Объект конфигурации config определяет основные параметры игры: тип рендерера, родительский DOM-элемент, размеры холста, цвет фона и главную сцену.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d88',
scene: Example
};
const game = new Phaser.Game(config);
Экземпляр Phaser.Game инициализируется с этой конфигурацией, запуская жизненный цикл сцены (preload, create, update). Установка backgroundColor помогает четко видеть границы игрового поля.
Что попробовать дальше
Методы setCrop() и setFlip() предоставляют мощный и производительный контроль над отображением спрайтов без необходимости редактирования исходных изображений. Вы можете экспериментировать: анимировать размер области обрезки для эффекта "проявления", использовать обрезку для отображения шкалы здоровья прямо на текстуре персонажа, комбинировать setCrop с масками для сложных эффектов или привязывать обрезку не к курсору, а к положению другого игрового объекта.
