О чем этот пример
Часто в играх необходимо, чтобы по одному и тому же элементу интерфейса или игровому объекту можно было кликнуть несколько раз, и каждый раз происходило одно и то же действие. Типичный пример — колода карт, из которой вытягивают карты, или набор одинаковых кнопок. В этом уроке мы разберем, как в Phaser 3 настроить обработку события 'отпускания' (`gameobjectup`) для множества динамически созданных объектов, используя один обработчик. Вы научитесь создавать интерактивные элементы, которые плавно исчезают при клике, и поймете принцип работы делегирования событий в контексте игровых объектов.
Версия 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 ()
{
this.createCards();
this.input.on('gameobjectup', function (pointer, gameObject)
{
this.tweens.add({
targets: gameObject,
alpha: 0,
scaleX: 0,
scaleY: 0
});
}, this);
}
createCards ()
{
const frames = this.textures.get('cards').getFrameNames();
for (let i = 0; i < 64; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
const s = Phaser.Math.FloatBetween(0.5, 1);
this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setScale(s).setInteractive();
}
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ассетов
Вся логика примера находится в классе сцены Example. На этапе предзагрузки (preload) мы указываем базовый URL для загрузки ресурсов и загружаем атлас текстур с картами. Атлас — это один изображение-спрайтшит (cards.png) и JSON-файл (cards.json), описывающий координаты отдельных кадров (карт) внутри него.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('cards', 'assets/atlas/cards.png', 'assets/atlas/cards.json');
}
Использование setBaseURL позволяет задать корневой путь для всех последующих загрузок, что удобно для организации ресурсов. Метод this.load.atlas загружает атлас под ключом 'cards'.
Динамическое создание интерактивных объектов
Основная логика создания 64 карт вынесена в метод createCards. Он вызывается из create. Для каждого объекта мы генерируем случайные координаты в пределах сцены (800x600) и случайный масштаб от 0.5 до 1, чтобы карты выглядели разнообразно.
createCards ()
{
const frames = this.textures.get('cards').getFrameNames();
for (let i = 0; i < 64; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
const s = Phaser.Math.FloatBetween(0.5, 1);
this.add.image(x, y, 'cards', Phaser.Math.RND.pick(frames)).setScale(s).setInteractive();
}
}
Ключевые моменты:
1. this.textures.get('cards').getFrameNames() — получает массив имен всех кадров (карт) из загруженного атласа.
2. Phaser.Math.RND.pick(frames) — случайным образом выбирает имя кадра для каждого экземпляра карты.
3. setInteractive() — делает изображение интерактивным, то есть способным реагировать на события ввода (клики, наведение). Без этого вызова событие gameobjectup не будет срабатывать.
Настройка единого обработчика событий
Вместо того чтобы назначать обработчик клика для каждой карты отдельно, мы используем мощную возможность Phaser — глобальный обработчик на уровне системы ввода (this.input). Событие 'gameobjectup' срабатывает, когда пользователь отпускает кнопку мыши или палец над любым интерактивным игровым объектом на сцене.
create ()
{
this.createCards();
this.input.on('gameobjectup', function (pointer, gameObject)
{
this.tweens.add({
targets: gameObject,
alpha: 0,
scaleX: 0,
scaleY: 0
});
}, this);
}
Как это работает:
- При возникновении события 'gameobjectup' система ввода автоматически передает в функцию-обработчик два аргумента: pointer (информация о точке ввода) и gameObject — ссылку именно на тот объект, над которым произошло событие.
- Внутри обработчика мы запускаем твин (анимацию) для этого конкретного gameObject. Твин одновременно изменяет прозрачность (alpha) и масштаб по осям X и Y до нуля, создавая эффект плавного исчезновения и сжатия.
- Третий параметр this в вызове this.input.on — это контекст выполнения для функции-обработчика. Он гарантирует, что внутри функции this будет указывать на текущую сцену, что позволяет нам использовать this.tweens.add.
Конфигурация и запуск игры
Стандартный блок конфигурации игры. Обратите внимание, что тип рендерера установлен в Phaser.WEBGL для лучшей производительности.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Свойство parent указывает ID HTML-элемента, в который будет встроен canvas игры. Если элемента с таким ID нет, Phaser создаст canvas прямо в body.
Что попробовать дальше
Использование глобального обработчика 'gameobjectup' — это эффективный и чистый способ реагировать на клики по множеству однотипных объектов. Он избавляет от необходимости вручную навешивать слушатели на каждый объект и автоматически предоставляет ссылку на целевой gameObject.
**Идеи для экспериментов:**
1. Измените логику в обработчике: например, при клике не удаляйте карту, а переворачивайте ее (setFlipX) или меняйте ее tint.
2. Попробуйте использовать другое событие ввода, например 'gameobjectdown' (нажатие) или 'gameobjectover' (наведение курсора).
3. Добавьте условие в обработчик: например, анимируйте исчезновение только карт определенной масти (проверяйте gameObject.frame.name).
4. Используйте pointer для получения координат клика и создавайте эффект "ряби" от этой точки.
