О чем этот пример
При разработке игр часто возникает ситуация, когда несколько игровых объектов перекрывают друг друга. По умолчанию Phaser обрабатывает только верхний объект в стеке, но что если вам нужна реакция всех объектов под указателем? Эта статья покажет, как использовать событие `pointerdown` для получения массива всех объектов под курсором и анимировать их одновременно. Этот подход полезен для карточных игр, пасьянсов, инвентарей или любых интерфейсов с наложенными элементами.
Версия 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.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
}
create ()
{
// Create a stack of random cards
const frames = this.textures.get('cards').getFrameNames();
let x = 80;
let y = 100;
for (let i = 0; i < 64; i++)
{
this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive();
x += 3;
y += 6;
}
this.input.setTopOnly(false);
this.input.on('pointerdown', function (pointer, gameObjects)
{
// gameObjects is an array of ALL GameObjects that were under the pointer
// So let's tween them all :)
if (gameObjects.length > 0)
{
this.tweens.add({
targets: gameObjects,
x: { value: 1100, duration: 1500, ease: 'Power2' },
delay: function (target, key, value, targetIndex)
{
return targetIndex * 100;
}
});
}
}, this);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1024,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание стопки карт
В методе preload загружается атлас с картами. В create создаётся стопка из 64 карт, каждая из которых смещается относительно предыдущей. Ключевой момент — каждый спрайт карты делается интерактивным с помощью метода .setInteractive(). Это необходимо, чтобы объекты могли реагировать на события ввода.
this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setInteractive();
Отключение режима «только верхний объект»
По умолчанию система ввода Phaser (this.input) находится в режиме «top-only». Это означает, что при клике возвращается только один, верхний, игровой объект под указателем. Для получения всех объектов нужно отключить этот режим.
this.input.setTopOnly(false);
После этого вызов события будет передавать в обработчик массив gameObjects, содержащий все интерактивные объекты под точкой клика.
Обработка события pointerdown и анимация объектов
На событие pointerdown вешается обработчик. Вторым аргументом функции (после pointer) передаётся массив gameObjects — все интерактивные спрайты, которые находились под курсором в момент клика.
this.input.on('pointerdown', function (pointer, gameObjects) {
// ... логика обработки
}, this);
Если массив не пустой, запускается твин для всех объектов сразу. Используется this.tweens.add, где targets — это весь массив gameObjects. Каждая карта будет двигаться к X=1100 с задержкой, зависящей от её индекса в массиве, создавая эффект последовательного движения.
delay: function (target, key, value, targetIndex) {
return targetIndex * 100;
}
Почему это работает: порядок объектов в массиве
Объекты в массиве gameObjects располагаются в порядке их отрисовки (render order). Первым элементом будет самый нижний объект в стеке, последним — самый верхний. Это важно учитывать при обработке, например, если логика должна зависеть от глубины объекта. В нашем примере задержка анимации (targetIndex * 100) как раз использует этот порядок: нижние карты начинают двигаться раньше верхних.
Что попробовать дальше
Использование setTopOnly(false) открывает возможности для сложных механик взаимодействия с наложенными объектами. Вы можете не только анимировать их, но и, например, применять эффекты, подсчитывать очки за групповой клик или реализовывать выделение нескольких карт. Попробуйте изменить пример: добавьте фильтрацию объектов по типу, реализуйте перетаскивание всей стопки или создайте эффект «разлёта» карт в разные стороны.
