О чем этот пример
В Phaser 3 свойство `scrollFactor` позволяет привязать движение объекта к перемещению камеры, создавая эффект параллакса. Но что происходит, когда такие объекты нужно перетаскивать мышью? Этот пример наглядно демонстрирует механику и тонкости интерактивного взаимодействия с объектами, у которых разная скорость прокрутки. Вы научитесь создавать перетаскиваемые элементы в многослойной сцене, правильно обрабатывать события и управлять визуальными состояниями.
Версия 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('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
create ()
{
const image1 = this.add.sprite(200, 300, 'eye').setInteractive();
const image2 = this.add.sprite(400, 300, 'eye').setInteractive();
const image3 = this.add.sprite(600, 300, 'eye').setInteractive();
this.input.setDraggable([ image1, image2, image3 ]);
image1.setScrollFactor(1);
image2.setScrollFactor(0.7);
image3.setScrollFactor(0.5);
this.input.on('gameobjectover', (pointer, gameObject) =>
{
gameObject.setTint(0x00ff00);
});
this.input.on('gameobjectout', (pointer, gameObject) =>
{
gameObject.clearTint();
});
this.input.on('dragstart', (pointer, gameObject) =>
{
gameObject.setTint(0xff0000);
});
this.input.on('drag', (pointer, gameObject, dragX, dragY) =>
{
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', (pointer, gameObject) =>
{
gameObject.clearTint();
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание интерактивных объектов
В методе preload загружается одно изображение, которое будет использоваться для всех спрайтов. Это позволяет быстро создавать однотипные объекты.
В методе create создаются три спрайта на одной горизонтальной линии. Ключевой момент — каждый из них сразу помечается как интерактивный с помощью метода .setInteractive(). Без этого объекты не смогут реагировать на события ввода (мышь, касание).
const image1 = this.add.sprite(200, 300, 'eye').setInteractive();
const image2 = this.add.sprite(400, 300, 'eye').setInteractive();
const image3 = this.add.sprite(600, 300, 'eye').setInteractive();
Далее все три спрайта добавляются в список перетаскиваемых объектов глобального менеджера ввода this.input. Это обязательный шаг для активации событий drag.
this.input.setDraggable([ image1, image2, image3 ]);
Настройка Scroll Factor и его влияние
Scroll Factor определяет, насколько быстро объект будет двигаться относительно камеры. Значение 1.0 означает, что объект движется с той же скоростью, что и камера (стандартный слой). Значения меньше единицы создают эффект фонового слоя, который движется медленнее.
image1.setScrollFactor(1);
image2.setScrollFactor(0.7);
image3.setScrollFactor(0.5);
Объект image3 с фактором 0.5 будет казаться самым дальним. Важно понимать, что scrollFactor влияет на отрисовку позиции объекта при движении камеры, но не меняет его реальные координаты в мире игры. Это становится критичным при перетаскивании, так как мы работаем именно с мировыми координатами gameObject.x и gameObject.y.
Обработка событий наведения и ухода курсора
Для улучшения пользовательского опыта объекты визуально реагируют на наведение курсора. Событие gameobjectover срабатывает, когда курсор оказывается над интерактивным объектом. В обработчике объект окрашивается в зеленый цвет с помощью setTint.
this.input.on('gameobjectover', (pointer, gameObject) => {
gameObject.setTint(0x00ff00);
});
Событие gameobjectout срабатывает при уходе курсора с объекта. Обработчик снимает tint, возвращая спрайту исходный вид.
this.input.on('gameobjectout', (pointer, gameObject) => {
gameObject.clearTint();
});
Эти события работают независимо от того, перетаскивается объект или нет.
Логика перетаскивания: начало, процесс и завершение
Механика перетаскивания разделена на три этапа, каждый из которых обрабатывается своим событием.
1. **dragstart**: Срабатывает в момент начала перетаскивания (кнопка мыши зажата над объектом). Объект окрашивается в красный цвет.
this.input.on('dragstart', (pointer, gameObject) => {
gameObject.setTint(0xff0000);
});
2. **drag**: Срабатывает непрерывно при перемещении мыши с зажатой кнопкой. В колбэк передаются целевые координаты dragX и dragY. Именно в них нужно установить позицию объекта, чтобы он следовал за курсором.
this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
gameObject.x = dragX;
gameObject.y = dragY;
});
3. **dragend**: Срабатывает при отпускании кнопки мыши. С объекта снимается красный tint. Зеленый tint при наведении будет применен заново, если курсор остался над объектом.
this.input.on('dragend', (pointer, gameObject) => {
gameObject.clearTint();
});
Координаты dragX/dragY уже являются мировыми координатами, что позволяет корректно перемещать объекты с разным scrollFactor.
Что попробовать дальше
Пример демонстрирует целостную систему перетаскивания в Phaser 3, которая корректно работает с многослойной графикой. Основной вывод: scrollFactor влияет только на визуальное отображение относительно камеры, а логика перетаскивания оперирует абсолютными мировыми координатами, что обеспечивает предсказуемое поведение. Для экспериментов попробуйте добавить движение камеры во время перетаскивания, изменить scrollFactor динамически или реализовать 'привязку' объекта к определенным слоям при отпускании.
